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.cjs
CHANGED
|
@@ -20,15 +20,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var index_exports = {};
|
|
21
21
|
__export(index_exports, {
|
|
22
22
|
AsyncLocalStorage: () => AsyncLocalStorage,
|
|
23
|
+
BaseSchemaDialect: () => BaseSchemaDialect,
|
|
23
24
|
DefaultBelongsToReference: () => DefaultBelongsToReference,
|
|
24
25
|
DefaultHasManyCollection: () => DefaultHasManyCollection,
|
|
25
26
|
DefaultManyToManyCollection: () => DefaultManyToManyCollection,
|
|
26
27
|
DeleteQueryBuilder: () => DeleteQueryBuilder,
|
|
27
28
|
EntityStatus: () => EntityStatus,
|
|
28
29
|
InsertQueryBuilder: () => InsertQueryBuilder,
|
|
30
|
+
MSSqlSchemaDialect: () => MSSqlSchemaDialect,
|
|
29
31
|
MySqlDialect: () => MySqlDialect,
|
|
32
|
+
MySqlSchemaDialect: () => MySqlSchemaDialect,
|
|
30
33
|
OrmContext: () => OrmContext,
|
|
34
|
+
PostgresDialect: () => PostgresDialect,
|
|
35
|
+
PostgresSchemaDialect: () => PostgresSchemaDialect,
|
|
31
36
|
RelationKinds: () => RelationKinds,
|
|
37
|
+
SQLiteSchemaDialect: () => SQLiteSchemaDialect,
|
|
32
38
|
SelectQueryBuilder: () => SelectQueryBuilder,
|
|
33
39
|
SqlServerDialect: () => SqlServerDialect,
|
|
34
40
|
SqliteDialect: () => SqliteDialect,
|
|
@@ -50,15 +56,22 @@ __export(index_exports, {
|
|
|
50
56
|
createLiteral: () => createLiteral,
|
|
51
57
|
defineTable: () => defineTable,
|
|
52
58
|
denseRank: () => denseRank,
|
|
59
|
+
deriveIndexName: () => deriveIndexName,
|
|
60
|
+
diffSchema: () => diffSchema,
|
|
53
61
|
eq: () => eq,
|
|
62
|
+
escapeLiteral: () => escapeLiteral,
|
|
54
63
|
executeHydrated: () => executeHydrated,
|
|
55
64
|
exists: () => exists,
|
|
56
65
|
firstValue: () => firstValue,
|
|
66
|
+
formatLiteral: () => formatLiteral,
|
|
67
|
+
generateCreateTableSql: () => generateCreateTableSql,
|
|
68
|
+
generateSchemaSql: () => generateSchemaSql,
|
|
57
69
|
gt: () => gt,
|
|
58
70
|
gte: () => gte,
|
|
59
71
|
hasMany: () => hasMany,
|
|
60
72
|
hydrateRows: () => hydrateRows,
|
|
61
73
|
inList: () => inList,
|
|
74
|
+
introspectSchema: () => introspectSchema,
|
|
62
75
|
isCaseExpressionNode: () => isCaseExpressionNode,
|
|
63
76
|
isExpressionSelectionNode: () => isExpressionSelectionNode,
|
|
64
77
|
isFunctionNode: () => isFunctionNode,
|
|
@@ -83,9 +96,14 @@ __export(index_exports, {
|
|
|
83
96
|
notLike: () => notLike,
|
|
84
97
|
ntile: () => ntile,
|
|
85
98
|
or: () => or,
|
|
99
|
+
quoteQualified: () => quoteQualified,
|
|
86
100
|
rank: () => rank,
|
|
101
|
+
renderColumnDefinition: () => renderColumnDefinition,
|
|
102
|
+
renderIndexColumns: () => renderIndexColumns,
|
|
103
|
+
resolvePrimaryKey: () => resolvePrimaryKey,
|
|
87
104
|
rowNumber: () => rowNumber,
|
|
88
105
|
sum: () => sum,
|
|
106
|
+
synchronizeSchema: () => synchronizeSchema,
|
|
89
107
|
valueToOperand: () => valueToOperand,
|
|
90
108
|
visitExpression: () => visitExpression,
|
|
91
109
|
visitOperand: () => visitOperand,
|
|
@@ -94,12 +112,25 @@ __export(index_exports, {
|
|
|
94
112
|
module.exports = __toCommonJS(index_exports);
|
|
95
113
|
|
|
96
114
|
// src/schema/table.ts
|
|
97
|
-
var defineTable = (name, columns, relations = {}, hooks) => {
|
|
115
|
+
var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
|
|
98
116
|
const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
|
|
99
117
|
acc[key] = { ...def, name: key, table: name };
|
|
100
118
|
return acc;
|
|
101
119
|
}, {});
|
|
102
|
-
return {
|
|
120
|
+
return {
|
|
121
|
+
name,
|
|
122
|
+
schema: options.schema,
|
|
123
|
+
columns: colsWithNames,
|
|
124
|
+
relations,
|
|
125
|
+
hooks,
|
|
126
|
+
primaryKey: options.primaryKey,
|
|
127
|
+
indexes: options.indexes,
|
|
128
|
+
checks: options.checks,
|
|
129
|
+
comment: options.comment,
|
|
130
|
+
engine: options.engine,
|
|
131
|
+
charset: options.charset,
|
|
132
|
+
collation: options.collation
|
|
133
|
+
};
|
|
103
134
|
};
|
|
104
135
|
|
|
105
136
|
// src/schema/column.ts
|
|
@@ -109,12 +140,52 @@ var col = {
|
|
|
109
140
|
* @returns ColumnDef with INT type
|
|
110
141
|
*/
|
|
111
142
|
int: () => ({ name: "", type: "INT" }),
|
|
143
|
+
/**
|
|
144
|
+
* Creates a big integer column definition
|
|
145
|
+
*/
|
|
146
|
+
bigint: () => ({ name: "", type: "BIGINT" }),
|
|
112
147
|
/**
|
|
113
148
|
* Creates a variable character column definition
|
|
114
149
|
* @param length - Maximum length of the string
|
|
115
150
|
* @returns ColumnDef with VARCHAR type
|
|
116
151
|
*/
|
|
117
152
|
varchar: (length) => ({ name: "", type: "VARCHAR", args: [length] }),
|
|
153
|
+
/**
|
|
154
|
+
* Creates a fixed precision decimal column definition
|
|
155
|
+
*/
|
|
156
|
+
decimal: (precision, scale = 0) => ({
|
|
157
|
+
name: "",
|
|
158
|
+
type: "DECIMAL",
|
|
159
|
+
args: [precision, scale]
|
|
160
|
+
}),
|
|
161
|
+
/**
|
|
162
|
+
* Creates a floating point column definition
|
|
163
|
+
*/
|
|
164
|
+
float: (precision) => ({
|
|
165
|
+
name: "",
|
|
166
|
+
type: "FLOAT",
|
|
167
|
+
args: precision !== void 0 ? [precision] : void 0
|
|
168
|
+
}),
|
|
169
|
+
/**
|
|
170
|
+
* Creates a UUID column definition
|
|
171
|
+
*/
|
|
172
|
+
uuid: () => ({ name: "", type: "UUID" }),
|
|
173
|
+
/**
|
|
174
|
+
* Creates a timestamp column definition
|
|
175
|
+
*/
|
|
176
|
+
timestamp: () => ({ name: "", type: "TIMESTAMP" }),
|
|
177
|
+
/**
|
|
178
|
+
* Creates a timestamptz column definition
|
|
179
|
+
*/
|
|
180
|
+
timestamptz: () => ({ name: "", type: "TIMESTAMPTZ" }),
|
|
181
|
+
/**
|
|
182
|
+
* Creates a date column definition
|
|
183
|
+
*/
|
|
184
|
+
date: () => ({ name: "", type: "DATE" }),
|
|
185
|
+
/**
|
|
186
|
+
* Creates a datetime column definition
|
|
187
|
+
*/
|
|
188
|
+
datetime: () => ({ name: "", type: "DATETIME" }),
|
|
118
189
|
/**
|
|
119
190
|
* Creates a JSON column definition
|
|
120
191
|
* @returns ColumnDef with JSON type
|
|
@@ -125,12 +196,64 @@ var col = {
|
|
|
125
196
|
* @returns ColumnDef with BOOLEAN type
|
|
126
197
|
*/
|
|
127
198
|
boolean: () => ({ name: "", type: "BOOLEAN" }),
|
|
199
|
+
/**
|
|
200
|
+
* Creates an enum column definition
|
|
201
|
+
* @param values - Enum values
|
|
202
|
+
*/
|
|
203
|
+
enum: (values) => ({ name: "", type: "ENUM", args: values }),
|
|
128
204
|
/**
|
|
129
205
|
* Marks a column definition as a primary key
|
|
130
206
|
* @param def - Column definition to modify
|
|
131
207
|
* @returns Modified ColumnDef with primary: true
|
|
132
208
|
*/
|
|
133
|
-
primaryKey: (def) => ({ ...def, primary: true })
|
|
209
|
+
primaryKey: (def) => ({ ...def, primary: true }),
|
|
210
|
+
/**
|
|
211
|
+
* Marks a column as NOT NULL
|
|
212
|
+
*/
|
|
213
|
+
notNull: (def) => ({ ...def, notNull: true }),
|
|
214
|
+
/**
|
|
215
|
+
* Marks a column as UNIQUE
|
|
216
|
+
*/
|
|
217
|
+
unique: (def, name) => ({
|
|
218
|
+
...def,
|
|
219
|
+
unique: name ?? true
|
|
220
|
+
}),
|
|
221
|
+
/**
|
|
222
|
+
* Sets a default value for the column
|
|
223
|
+
*/
|
|
224
|
+
default: (def, value) => ({
|
|
225
|
+
...def,
|
|
226
|
+
default: value
|
|
227
|
+
}),
|
|
228
|
+
/**
|
|
229
|
+
* Sets a raw SQL default value for the column
|
|
230
|
+
*/
|
|
231
|
+
defaultRaw: (def, expression) => ({
|
|
232
|
+
...def,
|
|
233
|
+
default: { raw: expression }
|
|
234
|
+
}),
|
|
235
|
+
/**
|
|
236
|
+
* Marks a column as auto-increment / identity
|
|
237
|
+
*/
|
|
238
|
+
autoIncrement: (def, strategy = "byDefault") => ({
|
|
239
|
+
...def,
|
|
240
|
+
autoIncrement: true,
|
|
241
|
+
generated: strategy
|
|
242
|
+
}),
|
|
243
|
+
/**
|
|
244
|
+
* Adds a foreign key reference
|
|
245
|
+
*/
|
|
246
|
+
references: (def, ref) => ({
|
|
247
|
+
...def,
|
|
248
|
+
references: ref
|
|
249
|
+
}),
|
|
250
|
+
/**
|
|
251
|
+
* Adds a check constraint to the column
|
|
252
|
+
*/
|
|
253
|
+
check: (def, expression) => ({
|
|
254
|
+
...def,
|
|
255
|
+
check: expression
|
|
256
|
+
})
|
|
134
257
|
};
|
|
135
258
|
|
|
136
259
|
// src/schema/relation.ts
|
|
@@ -540,6 +663,82 @@ var SelectQueryState = class _SelectQueryState {
|
|
|
540
663
|
ctes: [...this.ast.ctes ?? [], cte]
|
|
541
664
|
});
|
|
542
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* Adds a set operation (UNION/INTERSECT/EXCEPT) to the query
|
|
668
|
+
* @param op - Set operation node to add
|
|
669
|
+
* @returns New SelectQueryState with set operation
|
|
670
|
+
*/
|
|
671
|
+
withSetOperation(op) {
|
|
672
|
+
return this.clone({
|
|
673
|
+
...this.ast,
|
|
674
|
+
setOps: [...this.ast.setOps ?? [], op]
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
// src/core/ast/join-node.ts
|
|
680
|
+
var createJoinNode = (kind, tableName, condition, relationName) => ({
|
|
681
|
+
type: "Join",
|
|
682
|
+
kind,
|
|
683
|
+
table: { type: "Table", name: tableName },
|
|
684
|
+
condition,
|
|
685
|
+
relationName
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// src/core/sql/sql.ts
|
|
689
|
+
var SQL_OPERATORS = {
|
|
690
|
+
/** Equality operator */
|
|
691
|
+
EQUALS: "=",
|
|
692
|
+
/** Not equals operator */
|
|
693
|
+
NOT_EQUALS: "!=",
|
|
694
|
+
/** Greater than operator */
|
|
695
|
+
GREATER_THAN: ">",
|
|
696
|
+
/** Greater than or equal operator */
|
|
697
|
+
GREATER_OR_EQUAL: ">=",
|
|
698
|
+
/** Less than operator */
|
|
699
|
+
LESS_THAN: "<",
|
|
700
|
+
/** Less than or equal operator */
|
|
701
|
+
LESS_OR_EQUAL: "<=",
|
|
702
|
+
/** LIKE pattern matching operator */
|
|
703
|
+
LIKE: "LIKE",
|
|
704
|
+
/** NOT LIKE pattern matching operator */
|
|
705
|
+
NOT_LIKE: "NOT LIKE",
|
|
706
|
+
/** IN membership operator */
|
|
707
|
+
IN: "IN",
|
|
708
|
+
/** NOT IN membership operator */
|
|
709
|
+
NOT_IN: "NOT IN",
|
|
710
|
+
/** BETWEEN range operator */
|
|
711
|
+
BETWEEN: "BETWEEN",
|
|
712
|
+
/** NOT BETWEEN range operator */
|
|
713
|
+
NOT_BETWEEN: "NOT BETWEEN",
|
|
714
|
+
/** IS NULL null check operator */
|
|
715
|
+
IS_NULL: "IS NULL",
|
|
716
|
+
/** IS NOT NULL null check operator */
|
|
717
|
+
IS_NOT_NULL: "IS NOT NULL",
|
|
718
|
+
/** Logical AND operator */
|
|
719
|
+
AND: "AND",
|
|
720
|
+
/** Logical OR operator */
|
|
721
|
+
OR: "OR",
|
|
722
|
+
/** EXISTS operator */
|
|
723
|
+
EXISTS: "EXISTS",
|
|
724
|
+
/** NOT EXISTS operator */
|
|
725
|
+
NOT_EXISTS: "NOT EXISTS"
|
|
726
|
+
};
|
|
727
|
+
var JOIN_KINDS = {
|
|
728
|
+
/** INNER JOIN type */
|
|
729
|
+
INNER: "INNER",
|
|
730
|
+
/** LEFT JOIN type */
|
|
731
|
+
LEFT: "LEFT",
|
|
732
|
+
/** RIGHT JOIN type */
|
|
733
|
+
RIGHT: "RIGHT",
|
|
734
|
+
/** CROSS JOIN type */
|
|
735
|
+
CROSS: "CROSS"
|
|
736
|
+
};
|
|
737
|
+
var ORDER_DIRECTIONS = {
|
|
738
|
+
/** Ascending order */
|
|
739
|
+
ASC: "ASC",
|
|
740
|
+
/** Descending order */
|
|
741
|
+
DESC: "DESC"
|
|
543
742
|
};
|
|
544
743
|
|
|
545
744
|
// src/query-builder/hydration-manager.ts
|
|
@@ -591,8 +790,26 @@ var HydrationManager = class _HydrationManager {
|
|
|
591
790
|
* @returns AST with hydration metadata
|
|
592
791
|
*/
|
|
593
792
|
applyToAst(ast) {
|
|
793
|
+
if (ast.setOps && ast.setOps.length > 0) {
|
|
794
|
+
return ast;
|
|
795
|
+
}
|
|
594
796
|
const plan = this.planner.getPlan();
|
|
595
797
|
if (!plan) return ast;
|
|
798
|
+
const needsPaginationGuard = this.requiresParentPagination(ast, plan);
|
|
799
|
+
const rewritten = needsPaginationGuard ? this.wrapForParentPagination(ast, plan) : ast;
|
|
800
|
+
return this.attachHydrationMeta(rewritten, plan);
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Gets the current hydration plan
|
|
804
|
+
* @returns Hydration plan or undefined if none exists
|
|
805
|
+
*/
|
|
806
|
+
getPlan() {
|
|
807
|
+
return this.planner.getPlan();
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Attaches hydration metadata to a query AST node.
|
|
811
|
+
*/
|
|
812
|
+
attachHydrationMeta(ast, plan) {
|
|
596
813
|
return {
|
|
597
814
|
...ast,
|
|
598
815
|
meta: {
|
|
@@ -602,11 +819,154 @@ var HydrationManager = class _HydrationManager {
|
|
|
602
819
|
};
|
|
603
820
|
}
|
|
604
821
|
/**
|
|
605
|
-
*
|
|
606
|
-
*
|
|
822
|
+
* Determines whether the query needs pagination rewriting to keep LIMIT/OFFSET
|
|
823
|
+
* applied to parent rows when eager-loading multiplicative relations.
|
|
607
824
|
*/
|
|
608
|
-
|
|
609
|
-
|
|
825
|
+
requiresParentPagination(ast, plan) {
|
|
826
|
+
const hasPagination = ast.limit !== void 0 || ast.offset !== void 0;
|
|
827
|
+
return hasPagination && this.hasMultiplyingRelations(plan);
|
|
828
|
+
}
|
|
829
|
+
hasMultiplyingRelations(plan) {
|
|
830
|
+
return plan.relations.some(
|
|
831
|
+
(rel) => rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Rewrites the query using CTEs so LIMIT/OFFSET target distinct parent rows
|
|
836
|
+
* instead of the joined result set.
|
|
837
|
+
*
|
|
838
|
+
* The strategy:
|
|
839
|
+
* - Hoist the original query (minus limit/offset) into a base CTE.
|
|
840
|
+
* - Select distinct parent ids from that base CTE with the original ordering and pagination.
|
|
841
|
+
* - Join the base CTE against the paged ids to retrieve the joined rows for just that page.
|
|
842
|
+
*/
|
|
843
|
+
wrapForParentPagination(ast, plan) {
|
|
844
|
+
const projectionNames = this.getProjectionNames(ast.columns);
|
|
845
|
+
if (!projectionNames) {
|
|
846
|
+
return ast;
|
|
847
|
+
}
|
|
848
|
+
const projectionAliases = this.buildProjectionAliasMap(ast.columns);
|
|
849
|
+
const projectionSet = new Set(projectionNames);
|
|
850
|
+
const rootPkAlias = projectionAliases.get(`${plan.rootTable}.${plan.rootPrimaryKey}`) ?? plan.rootPrimaryKey;
|
|
851
|
+
const baseCteName = this.nextCteName(ast.ctes, "__metal_pagination_base");
|
|
852
|
+
const baseQuery = {
|
|
853
|
+
...ast,
|
|
854
|
+
ctes: void 0,
|
|
855
|
+
limit: void 0,
|
|
856
|
+
offset: void 0,
|
|
857
|
+
orderBy: void 0,
|
|
858
|
+
meta: void 0
|
|
859
|
+
};
|
|
860
|
+
const baseCte = {
|
|
861
|
+
type: "CommonTableExpression",
|
|
862
|
+
name: baseCteName,
|
|
863
|
+
query: baseQuery,
|
|
864
|
+
recursive: false
|
|
865
|
+
};
|
|
866
|
+
const orderBy = this.mapOrderBy(ast.orderBy, plan, projectionAliases, baseCteName, projectionSet);
|
|
867
|
+
if (orderBy === null) {
|
|
868
|
+
return ast;
|
|
869
|
+
}
|
|
870
|
+
const pageCteName = this.nextCteName([...ast.ctes ?? [], baseCte], "__metal_pagination_page");
|
|
871
|
+
const pagingColumns = this.buildPagingColumns(rootPkAlias, orderBy, baseCteName);
|
|
872
|
+
const pageCte = {
|
|
873
|
+
type: "CommonTableExpression",
|
|
874
|
+
name: pageCteName,
|
|
875
|
+
query: {
|
|
876
|
+
type: "SelectQuery",
|
|
877
|
+
from: { type: "Table", name: baseCteName },
|
|
878
|
+
columns: pagingColumns,
|
|
879
|
+
joins: [],
|
|
880
|
+
distinct: [{ type: "Column", table: baseCteName, name: rootPkAlias }],
|
|
881
|
+
orderBy,
|
|
882
|
+
limit: ast.limit,
|
|
883
|
+
offset: ast.offset
|
|
884
|
+
},
|
|
885
|
+
recursive: false
|
|
886
|
+
};
|
|
887
|
+
const joinCondition = eq(
|
|
888
|
+
{ type: "Column", table: baseCteName, name: rootPkAlias },
|
|
889
|
+
{ type: "Column", table: pageCteName, name: rootPkAlias }
|
|
890
|
+
);
|
|
891
|
+
const outerColumns = projectionNames.map((name) => ({
|
|
892
|
+
type: "Column",
|
|
893
|
+
table: baseCteName,
|
|
894
|
+
name,
|
|
895
|
+
alias: name
|
|
896
|
+
}));
|
|
897
|
+
return {
|
|
898
|
+
type: "SelectQuery",
|
|
899
|
+
from: { type: "Table", name: baseCteName },
|
|
900
|
+
columns: outerColumns,
|
|
901
|
+
joins: [createJoinNode(JOIN_KINDS.INNER, pageCteName, joinCondition)],
|
|
902
|
+
orderBy,
|
|
903
|
+
ctes: [...ast.ctes ?? [], baseCte, pageCte]
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
nextCteName(existing, baseName) {
|
|
907
|
+
const names = new Set((existing ?? []).map((cte) => cte.name));
|
|
908
|
+
let candidate = baseName;
|
|
909
|
+
let suffix = 1;
|
|
910
|
+
while (names.has(candidate)) {
|
|
911
|
+
suffix += 1;
|
|
912
|
+
candidate = `${baseName}_${suffix}`;
|
|
913
|
+
}
|
|
914
|
+
return candidate;
|
|
915
|
+
}
|
|
916
|
+
getProjectionNames(columns) {
|
|
917
|
+
const names = [];
|
|
918
|
+
for (const col2 of columns) {
|
|
919
|
+
const alias = col2.alias ?? col2.name;
|
|
920
|
+
if (!alias) return void 0;
|
|
921
|
+
names.push(alias);
|
|
922
|
+
}
|
|
923
|
+
return names;
|
|
924
|
+
}
|
|
925
|
+
buildProjectionAliasMap(columns) {
|
|
926
|
+
const map = /* @__PURE__ */ new Map();
|
|
927
|
+
for (const col2 of columns) {
|
|
928
|
+
if (col2.type !== "Column") continue;
|
|
929
|
+
const node = col2;
|
|
930
|
+
const key = `${node.table}.${node.name}`;
|
|
931
|
+
map.set(key, node.alias ?? node.name);
|
|
932
|
+
}
|
|
933
|
+
return map;
|
|
934
|
+
}
|
|
935
|
+
mapOrderBy(orderBy, plan, projectionAliases, baseAlias, availableColumns) {
|
|
936
|
+
if (!orderBy || orderBy.length === 0) {
|
|
937
|
+
return void 0;
|
|
938
|
+
}
|
|
939
|
+
const mapped = [];
|
|
940
|
+
for (const ob of orderBy) {
|
|
941
|
+
if (ob.column.table !== plan.rootTable) {
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
const alias = projectionAliases.get(`${ob.column.table}.${ob.column.name}`) ?? ob.column.name;
|
|
945
|
+
if (!availableColumns.has(alias)) {
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
mapped.push({
|
|
949
|
+
type: "OrderBy",
|
|
950
|
+
column: { type: "Column", table: baseAlias, name: alias },
|
|
951
|
+
direction: ob.direction
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
return mapped;
|
|
955
|
+
}
|
|
956
|
+
buildPagingColumns(primaryKey, orderBy, tableAlias) {
|
|
957
|
+
const columns = [{ type: "Column", table: tableAlias, name: primaryKey, alias: primaryKey }];
|
|
958
|
+
if (!orderBy) return columns;
|
|
959
|
+
for (const ob of orderBy) {
|
|
960
|
+
if (!columns.some((col2) => col2.name === ob.column.name)) {
|
|
961
|
+
columns.push({
|
|
962
|
+
type: "Column",
|
|
963
|
+
table: tableAlias,
|
|
964
|
+
name: ob.column.name,
|
|
965
|
+
alias: ob.column.name
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return columns;
|
|
610
970
|
}
|
|
611
971
|
};
|
|
612
972
|
|
|
@@ -869,6 +1229,20 @@ var QueryAstService = class {
|
|
|
869
1229
|
};
|
|
870
1230
|
return this.state.withCte(cte);
|
|
871
1231
|
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Adds a set operation (UNION/UNION ALL/INTERSECT/EXCEPT) to the query
|
|
1234
|
+
* @param operator - Set operator
|
|
1235
|
+
* @param query - Right-hand side query
|
|
1236
|
+
* @returns Updated query state with set operation
|
|
1237
|
+
*/
|
|
1238
|
+
withSetOperation(operator, query) {
|
|
1239
|
+
const op = {
|
|
1240
|
+
type: "SetOperation",
|
|
1241
|
+
operator,
|
|
1242
|
+
query
|
|
1243
|
+
};
|
|
1244
|
+
return this.state.withSetOperation(op);
|
|
1245
|
+
}
|
|
872
1246
|
/**
|
|
873
1247
|
* Selects a subquery as a column
|
|
874
1248
|
* @param alias - Alias for the subquery
|
|
@@ -1021,15 +1395,6 @@ var RelationProjectionHelper = class {
|
|
|
1021
1395
|
}
|
|
1022
1396
|
};
|
|
1023
1397
|
|
|
1024
|
-
// src/core/ast/join-node.ts
|
|
1025
|
-
var createJoinNode = (kind, tableName, condition, relationName) => ({
|
|
1026
|
-
type: "Join",
|
|
1027
|
-
kind,
|
|
1028
|
-
table: { type: "Table", name: tableName },
|
|
1029
|
-
condition,
|
|
1030
|
-
relationName
|
|
1031
|
-
});
|
|
1032
|
-
|
|
1033
1398
|
// src/query-builder/relation-conditions.ts
|
|
1034
1399
|
var assertNever = (value) => {
|
|
1035
1400
|
throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
|
|
@@ -1085,62 +1450,6 @@ var buildRelationCorrelation = (root, relation) => {
|
|
|
1085
1450
|
return baseRelationCondition(root, relation);
|
|
1086
1451
|
};
|
|
1087
1452
|
|
|
1088
|
-
// src/core/sql/sql.ts
|
|
1089
|
-
var SQL_OPERATORS = {
|
|
1090
|
-
/** Equality operator */
|
|
1091
|
-
EQUALS: "=",
|
|
1092
|
-
/** Not equals operator */
|
|
1093
|
-
NOT_EQUALS: "!=",
|
|
1094
|
-
/** Greater than operator */
|
|
1095
|
-
GREATER_THAN: ">",
|
|
1096
|
-
/** Greater than or equal operator */
|
|
1097
|
-
GREATER_OR_EQUAL: ">=",
|
|
1098
|
-
/** Less than operator */
|
|
1099
|
-
LESS_THAN: "<",
|
|
1100
|
-
/** Less than or equal operator */
|
|
1101
|
-
LESS_OR_EQUAL: "<=",
|
|
1102
|
-
/** LIKE pattern matching operator */
|
|
1103
|
-
LIKE: "LIKE",
|
|
1104
|
-
/** NOT LIKE pattern matching operator */
|
|
1105
|
-
NOT_LIKE: "NOT LIKE",
|
|
1106
|
-
/** IN membership operator */
|
|
1107
|
-
IN: "IN",
|
|
1108
|
-
/** NOT IN membership operator */
|
|
1109
|
-
NOT_IN: "NOT IN",
|
|
1110
|
-
/** BETWEEN range operator */
|
|
1111
|
-
BETWEEN: "BETWEEN",
|
|
1112
|
-
/** NOT BETWEEN range operator */
|
|
1113
|
-
NOT_BETWEEN: "NOT BETWEEN",
|
|
1114
|
-
/** IS NULL null check operator */
|
|
1115
|
-
IS_NULL: "IS NULL",
|
|
1116
|
-
/** IS NOT NULL null check operator */
|
|
1117
|
-
IS_NOT_NULL: "IS NOT NULL",
|
|
1118
|
-
/** Logical AND operator */
|
|
1119
|
-
AND: "AND",
|
|
1120
|
-
/** Logical OR operator */
|
|
1121
|
-
OR: "OR",
|
|
1122
|
-
/** EXISTS operator */
|
|
1123
|
-
EXISTS: "EXISTS",
|
|
1124
|
-
/** NOT EXISTS operator */
|
|
1125
|
-
NOT_EXISTS: "NOT EXISTS"
|
|
1126
|
-
};
|
|
1127
|
-
var JOIN_KINDS = {
|
|
1128
|
-
/** INNER JOIN type */
|
|
1129
|
-
INNER: "INNER",
|
|
1130
|
-
/** LEFT JOIN type */
|
|
1131
|
-
LEFT: "LEFT",
|
|
1132
|
-
/** RIGHT JOIN type */
|
|
1133
|
-
RIGHT: "RIGHT",
|
|
1134
|
-
/** CROSS JOIN type */
|
|
1135
|
-
CROSS: "CROSS"
|
|
1136
|
-
};
|
|
1137
|
-
var ORDER_DIRECTIONS = {
|
|
1138
|
-
/** Ascending order */
|
|
1139
|
-
ASC: "ASC",
|
|
1140
|
-
/** Descending order */
|
|
1141
|
-
DESC: "DESC"
|
|
1142
|
-
};
|
|
1143
|
-
|
|
1144
1453
|
// src/query-builder/relation-service.ts
|
|
1145
1454
|
var RelationService = class {
|
|
1146
1455
|
/**
|
|
@@ -1586,6 +1895,16 @@ var hasEntityMeta = (entity) => {
|
|
|
1586
1895
|
|
|
1587
1896
|
// src/orm/relations/has-many.ts
|
|
1588
1897
|
var toKey2 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
1898
|
+
var hideInternal = (obj, keys) => {
|
|
1899
|
+
for (const key of keys) {
|
|
1900
|
+
Object.defineProperty(obj, key, {
|
|
1901
|
+
value: obj[key],
|
|
1902
|
+
writable: false,
|
|
1903
|
+
configurable: false,
|
|
1904
|
+
enumerable: false
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
};
|
|
1589
1908
|
var DefaultHasManyCollection = class {
|
|
1590
1909
|
constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
|
|
1591
1910
|
this.ctx = ctx;
|
|
@@ -1601,6 +1920,7 @@ var DefaultHasManyCollection = class {
|
|
|
1601
1920
|
this.items = [];
|
|
1602
1921
|
this.added = /* @__PURE__ */ new Set();
|
|
1603
1922
|
this.removed = /* @__PURE__ */ new Set();
|
|
1923
|
+
hideInternal(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
|
|
1604
1924
|
this.hydrateFromCache();
|
|
1605
1925
|
}
|
|
1606
1926
|
async load() {
|
|
@@ -1676,10 +1996,23 @@ var DefaultHasManyCollection = class {
|
|
|
1676
1996
|
this.items = rows.map((row) => this.createEntity(row));
|
|
1677
1997
|
this.loaded = true;
|
|
1678
1998
|
}
|
|
1999
|
+
toJSON() {
|
|
2000
|
+
return this.items;
|
|
2001
|
+
}
|
|
1679
2002
|
};
|
|
1680
2003
|
|
|
1681
2004
|
// src/orm/relations/belongs-to.ts
|
|
1682
2005
|
var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
2006
|
+
var hideInternal2 = (obj, keys) => {
|
|
2007
|
+
for (const key of keys) {
|
|
2008
|
+
Object.defineProperty(obj, key, {
|
|
2009
|
+
value: obj[key],
|
|
2010
|
+
writable: false,
|
|
2011
|
+
configurable: false,
|
|
2012
|
+
enumerable: false
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
1683
2016
|
var DefaultBelongsToReference = class {
|
|
1684
2017
|
constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
|
|
1685
2018
|
this.ctx = ctx;
|
|
@@ -1693,6 +2026,7 @@ var DefaultBelongsToReference = class {
|
|
|
1693
2026
|
this.targetKey = targetKey;
|
|
1694
2027
|
this.loaded = false;
|
|
1695
2028
|
this.current = null;
|
|
2029
|
+
hideInternal2(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
|
|
1696
2030
|
this.populateFromHydrationCache();
|
|
1697
2031
|
}
|
|
1698
2032
|
async load() {
|
|
@@ -1753,10 +2087,23 @@ var DefaultBelongsToReference = class {
|
|
|
1753
2087
|
this.current = this.createEntity(row);
|
|
1754
2088
|
this.loaded = true;
|
|
1755
2089
|
}
|
|
2090
|
+
toJSON() {
|
|
2091
|
+
return this.current;
|
|
2092
|
+
}
|
|
1756
2093
|
};
|
|
1757
2094
|
|
|
1758
2095
|
// src/orm/relations/many-to-many.ts
|
|
1759
2096
|
var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
2097
|
+
var hideInternal3 = (obj, keys) => {
|
|
2098
|
+
for (const key of keys) {
|
|
2099
|
+
Object.defineProperty(obj, key, {
|
|
2100
|
+
value: obj[key],
|
|
2101
|
+
writable: false,
|
|
2102
|
+
configurable: false,
|
|
2103
|
+
enumerable: false
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
1760
2107
|
var DefaultManyToManyCollection = class {
|
|
1761
2108
|
constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
|
|
1762
2109
|
this.ctx = ctx;
|
|
@@ -1770,6 +2117,7 @@ var DefaultManyToManyCollection = class {
|
|
|
1770
2117
|
this.localKey = localKey;
|
|
1771
2118
|
this.loaded = false;
|
|
1772
2119
|
this.items = [];
|
|
2120
|
+
hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
|
|
1773
2121
|
this.hydrateFromCache();
|
|
1774
2122
|
}
|
|
1775
2123
|
async load() {
|
|
@@ -1875,6 +2223,9 @@ var DefaultManyToManyCollection = class {
|
|
|
1875
2223
|
});
|
|
1876
2224
|
this.loaded = true;
|
|
1877
2225
|
}
|
|
2226
|
+
toJSON() {
|
|
2227
|
+
return this.items;
|
|
2228
|
+
}
|
|
1878
2229
|
};
|
|
1879
2230
|
|
|
1880
2231
|
// src/orm/lazy-batch.ts
|
|
@@ -2235,9 +2586,15 @@ var flattenResults = (results) => {
|
|
|
2235
2586
|
return rows;
|
|
2236
2587
|
};
|
|
2237
2588
|
async function executeHydrated(ctx, qb) {
|
|
2238
|
-
const
|
|
2589
|
+
const ast = qb.getAST();
|
|
2590
|
+
const compiled = ctx.dialect.compileSelect(ast);
|
|
2239
2591
|
const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
|
|
2240
2592
|
const rows = flattenResults(executed);
|
|
2593
|
+
if (ast.setOps && ast.setOps.length > 0) {
|
|
2594
|
+
return rows.map(
|
|
2595
|
+
(row) => createEntityProxy(ctx, qb.getTable(), row, qb.getLazyRelations())
|
|
2596
|
+
);
|
|
2597
|
+
}
|
|
2241
2598
|
const hydrated = hydrateRows(rows, qb.getHydrationPlan());
|
|
2242
2599
|
return hydrated.map(
|
|
2243
2600
|
(row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
|
|
@@ -2284,6 +2641,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
2284
2641
|
const joinNode = createJoinNode(kind, table.name, condition);
|
|
2285
2642
|
return this.applyAst(context, (service) => service.withJoin(joinNode));
|
|
2286
2643
|
}
|
|
2644
|
+
applySetOperation(operator, query) {
|
|
2645
|
+
const subAst = this.resolveQueryNode(query);
|
|
2646
|
+
return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
|
|
2647
|
+
}
|
|
2287
2648
|
/**
|
|
2288
2649
|
* Selects specific columns for the query
|
|
2289
2650
|
* @param columns - Record of column definitions, function nodes, case expressions, or window functions
|
|
@@ -2472,6 +2833,38 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
2472
2833
|
const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
|
|
2473
2834
|
return this.clone(nextContext);
|
|
2474
2835
|
}
|
|
2836
|
+
/**
|
|
2837
|
+
* Combines this query with another using UNION
|
|
2838
|
+
* @param query - Query to union with
|
|
2839
|
+
* @returns New query builder instance with the set operation
|
|
2840
|
+
*/
|
|
2841
|
+
union(query) {
|
|
2842
|
+
return this.clone(this.applySetOperation("UNION", query));
|
|
2843
|
+
}
|
|
2844
|
+
/**
|
|
2845
|
+
* Combines this query with another using UNION ALL
|
|
2846
|
+
* @param query - Query to union with
|
|
2847
|
+
* @returns New query builder instance with the set operation
|
|
2848
|
+
*/
|
|
2849
|
+
unionAll(query) {
|
|
2850
|
+
return this.clone(this.applySetOperation("UNION ALL", query));
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
2853
|
+
* Combines this query with another using INTERSECT
|
|
2854
|
+
* @param query - Query to intersect with
|
|
2855
|
+
* @returns New query builder instance with the set operation
|
|
2856
|
+
*/
|
|
2857
|
+
intersect(query) {
|
|
2858
|
+
return this.clone(this.applySetOperation("INTERSECT", query));
|
|
2859
|
+
}
|
|
2860
|
+
/**
|
|
2861
|
+
* Combines this query with another using EXCEPT
|
|
2862
|
+
* @param query - Query to subtract
|
|
2863
|
+
* @returns New query builder instance with the set operation
|
|
2864
|
+
*/
|
|
2865
|
+
except(query) {
|
|
2866
|
+
return this.clone(this.applySetOperation("EXCEPT", query));
|
|
2867
|
+
}
|
|
2475
2868
|
/**
|
|
2476
2869
|
* Adds a WHERE EXISTS condition to the query
|
|
2477
2870
|
* @param subquery - Subquery to check for existence
|
|
@@ -2761,7 +3154,8 @@ var Dialect = class {
|
|
|
2761
3154
|
*/
|
|
2762
3155
|
compileSelect(ast) {
|
|
2763
3156
|
const ctx = this.createCompilerContext();
|
|
2764
|
-
const
|
|
3157
|
+
const normalized = this.normalizeSelectAst(ast);
|
|
3158
|
+
const rawSql = this.compileSelectAst(normalized, ctx).trim();
|
|
2765
3159
|
const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
|
|
2766
3160
|
return {
|
|
2767
3161
|
sql,
|
|
@@ -2795,6 +3189,9 @@ var Dialect = class {
|
|
|
2795
3189
|
params: [...ctx.params]
|
|
2796
3190
|
};
|
|
2797
3191
|
}
|
|
3192
|
+
supportsReturning() {
|
|
3193
|
+
return false;
|
|
3194
|
+
}
|
|
2798
3195
|
/**
|
|
2799
3196
|
* Compiles a WHERE clause
|
|
2800
3197
|
* @param where - WHERE expression
|
|
@@ -2819,7 +3216,11 @@ var Dialect = class {
|
|
|
2819
3216
|
* @returns SQL for EXISTS subquery
|
|
2820
3217
|
*/
|
|
2821
3218
|
compileSelectForExists(ast, ctx) {
|
|
2822
|
-
const
|
|
3219
|
+
const normalized = this.normalizeSelectAst(ast);
|
|
3220
|
+
const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
|
|
3221
|
+
if (normalized.setOps && normalized.setOps.length > 0) {
|
|
3222
|
+
return `SELECT 1 FROM (${full}) AS _exists`;
|
|
3223
|
+
}
|
|
2823
3224
|
const upper = full.toUpperCase();
|
|
2824
3225
|
const fromIndex = upper.indexOf(" FROM ");
|
|
2825
3226
|
if (fromIndex === -1) {
|
|
@@ -2852,6 +3253,65 @@ var Dialect = class {
|
|
|
2852
3253
|
formatPlaceholder(index) {
|
|
2853
3254
|
return "?";
|
|
2854
3255
|
}
|
|
3256
|
+
/**
|
|
3257
|
+
* Whether the current dialect supports a given set operation.
|
|
3258
|
+
* Override in concrete dialects to restrict support.
|
|
3259
|
+
*/
|
|
3260
|
+
supportsSetOperation(kind) {
|
|
3261
|
+
return true;
|
|
3262
|
+
}
|
|
3263
|
+
/**
|
|
3264
|
+
* Validates set-operation semantics:
|
|
3265
|
+
* - Ensures the dialect supports requested operators.
|
|
3266
|
+
* - Enforces that only the outermost compound query may have ORDER/LIMIT/OFFSET.
|
|
3267
|
+
* @param ast - Query to validate
|
|
3268
|
+
* @param isOutermost - Whether this node is the outermost compound query
|
|
3269
|
+
*/
|
|
3270
|
+
validateSetOperations(ast, isOutermost = true) {
|
|
3271
|
+
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
3272
|
+
if (!isOutermost && (ast.orderBy || ast.limit !== void 0 || ast.offset !== void 0)) {
|
|
3273
|
+
throw new Error("ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.");
|
|
3274
|
+
}
|
|
3275
|
+
if (hasSetOps) {
|
|
3276
|
+
for (const op of ast.setOps) {
|
|
3277
|
+
if (!this.supportsSetOperation(op.operator)) {
|
|
3278
|
+
throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
|
|
3279
|
+
}
|
|
3280
|
+
this.validateSetOperations(op.query, false);
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
/**
|
|
3285
|
+
* Hoists CTEs from set-operation operands to the outermost query so WITH appears once.
|
|
3286
|
+
* @param ast - Query AST
|
|
3287
|
+
* @returns Normalized AST without inner CTEs and a list of hoisted CTEs
|
|
3288
|
+
*/
|
|
3289
|
+
hoistCtes(ast) {
|
|
3290
|
+
let hoisted = [];
|
|
3291
|
+
const normalizedSetOps = ast.setOps?.map((op) => {
|
|
3292
|
+
const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
|
|
3293
|
+
const childCtes = child.ctes ?? [];
|
|
3294
|
+
if (childCtes.length) {
|
|
3295
|
+
hoisted = hoisted.concat(childCtes);
|
|
3296
|
+
}
|
|
3297
|
+
hoisted = hoisted.concat(childHoisted);
|
|
3298
|
+
const queryWithoutCtes = childCtes.length ? { ...child, ctes: void 0 } : child;
|
|
3299
|
+
return { ...op, query: queryWithoutCtes };
|
|
3300
|
+
});
|
|
3301
|
+
const normalized = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
|
|
3302
|
+
return { normalized, hoistedCtes: hoisted };
|
|
3303
|
+
}
|
|
3304
|
+
/**
|
|
3305
|
+
* Normalizes a SELECT AST before compilation (validation + CTE hoisting).
|
|
3306
|
+
* @param ast - Query AST
|
|
3307
|
+
* @returns Normalized query AST
|
|
3308
|
+
*/
|
|
3309
|
+
normalizeSelectAst(ast) {
|
|
3310
|
+
this.validateSetOperations(ast, true);
|
|
3311
|
+
const { normalized, hoistedCtes } = this.hoistCtes(ast);
|
|
3312
|
+
const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
|
|
3313
|
+
return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
|
|
3314
|
+
}
|
|
2855
3315
|
constructor() {
|
|
2856
3316
|
this.expressionCompilers = /* @__PURE__ */ new Map();
|
|
2857
3317
|
this.operandCompilers = /* @__PURE__ */ new Map();
|
|
@@ -2994,102 +3454,181 @@ var Dialect = class {
|
|
|
2994
3454
|
}
|
|
2995
3455
|
};
|
|
2996
3456
|
|
|
2997
|
-
// src/core/dialect/
|
|
2998
|
-
var
|
|
3457
|
+
// src/core/dialect/base/sql-dialect.ts
|
|
3458
|
+
var SqlDialectBase = class extends Dialect {
|
|
2999
3459
|
/**
|
|
3000
|
-
*
|
|
3460
|
+
* Compiles SELECT query AST to SQL using common rules.
|
|
3001
3461
|
*/
|
|
3002
|
-
|
|
3003
|
-
|
|
3462
|
+
compileSelectAst(ast, ctx) {
|
|
3463
|
+
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
3464
|
+
const ctes = this.compileCtes(ast, ctx);
|
|
3465
|
+
const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
|
|
3466
|
+
const baseSelect = this.compileSelectCore(baseAst, ctx);
|
|
3467
|
+
if (!hasSetOps) {
|
|
3468
|
+
return `${ctes}${baseSelect}`;
|
|
3469
|
+
}
|
|
3470
|
+
const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
|
|
3471
|
+
const orderBy = this.compileOrderBy(ast);
|
|
3472
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
3473
|
+
const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
|
|
3474
|
+
return `${ctes}${combined}${orderBy}${pagination}`;
|
|
3475
|
+
}
|
|
3476
|
+
compileInsertAst(ast, ctx) {
|
|
3477
|
+
const table = this.compileTableName(ast.into);
|
|
3478
|
+
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
3479
|
+
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
3480
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
3481
|
+
return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
|
|
3004
3482
|
}
|
|
3005
3483
|
/**
|
|
3006
|
-
*
|
|
3007
|
-
* @param id - Identifier to quote
|
|
3008
|
-
* @returns Quoted identifier
|
|
3484
|
+
* Compiles a single SELECT (no set operations, no CTE prefix).
|
|
3009
3485
|
*/
|
|
3010
|
-
|
|
3011
|
-
|
|
3486
|
+
compileSelectCore(ast, ctx) {
|
|
3487
|
+
const columns = this.compileSelectColumns(ast, ctx);
|
|
3488
|
+
const from = this.compileFrom(ast.from);
|
|
3489
|
+
const joins = this.compileJoins(ast, ctx);
|
|
3490
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3491
|
+
const groupBy = this.compileGroupBy(ast);
|
|
3492
|
+
const having = this.compileHaving(ast, ctx);
|
|
3493
|
+
const orderBy = this.compileOrderBy(ast);
|
|
3494
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
3495
|
+
return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
|
|
3496
|
+
}
|
|
3497
|
+
compileUpdateAst(ast, ctx) {
|
|
3498
|
+
const table = this.compileTableName(ast.table);
|
|
3499
|
+
const assignments = ast.set.map((assignment) => {
|
|
3500
|
+
const col2 = assignment.column;
|
|
3501
|
+
const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
|
|
3502
|
+
const value = this.compileOperand(assignment.value, ctx);
|
|
3503
|
+
return `${target} = ${value}`;
|
|
3504
|
+
}).join(", ");
|
|
3505
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3506
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
3507
|
+
return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
|
|
3508
|
+
}
|
|
3509
|
+
compileDeleteAst(ast, ctx) {
|
|
3510
|
+
const table = this.compileTableName(ast.from);
|
|
3511
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3512
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
3513
|
+
return `DELETE FROM ${table}${whereClause}${returning}`;
|
|
3012
3514
|
}
|
|
3013
3515
|
/**
|
|
3014
|
-
*
|
|
3015
|
-
* @param node - JSON path node
|
|
3016
|
-
* @returns MySQL JSON path expression
|
|
3516
|
+
* Default RETURNING compilation: no support.
|
|
3017
3517
|
*/
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3518
|
+
compileReturning(returning, _ctx) {
|
|
3519
|
+
if (!returning || returning.length === 0) return "";
|
|
3520
|
+
throw new Error("RETURNING is not supported by this dialect.");
|
|
3521
|
+
}
|
|
3522
|
+
formatReturningColumns(returning) {
|
|
3523
|
+
return returning.map((column) => {
|
|
3524
|
+
const tablePart = column.table ? `${this.quoteIdentifier(column.table)}.` : "";
|
|
3525
|
+
const aliasPart = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : "";
|
|
3526
|
+
return `${tablePart}${this.quoteIdentifier(column.name)}${aliasPart}`;
|
|
3527
|
+
}).join(", ");
|
|
3021
3528
|
}
|
|
3022
3529
|
/**
|
|
3023
|
-
*
|
|
3024
|
-
* @param ast - Query AST
|
|
3025
|
-
* @param ctx - Compiler context
|
|
3026
|
-
* @returns MySQL SQL string
|
|
3530
|
+
* DISTINCT clause. Override for DISTINCT ON support.
|
|
3027
3531
|
*/
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
|
|
3035
|
-
} else if (c.type === "ScalarSubquery") {
|
|
3036
|
-
expr = this.compileOperand(c, ctx);
|
|
3037
|
-
} else if (c.type === "WindowFunction") {
|
|
3038
|
-
expr = this.compileOperand(c, ctx);
|
|
3039
|
-
}
|
|
3532
|
+
compileDistinct(ast) {
|
|
3533
|
+
return ast.distinct ? "DISTINCT " : "";
|
|
3534
|
+
}
|
|
3535
|
+
compileSelectColumns(ast, ctx) {
|
|
3536
|
+
return ast.columns.map((c) => {
|
|
3537
|
+
const expr = this.compileOperand(c, ctx);
|
|
3040
3538
|
if (c.alias) {
|
|
3041
3539
|
if (c.alias.includes("(")) return c.alias;
|
|
3042
3540
|
return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
|
|
3043
3541
|
}
|
|
3044
3542
|
return expr;
|
|
3045
3543
|
}).join(", ");
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
const
|
|
3049
|
-
|
|
3544
|
+
}
|
|
3545
|
+
compileFrom(ast) {
|
|
3546
|
+
const base = this.compileTableName(ast);
|
|
3547
|
+
return ast.alias ? `${base} AS ${this.quoteIdentifier(ast.alias)}` : base;
|
|
3548
|
+
}
|
|
3549
|
+
compileTableName(table) {
|
|
3550
|
+
if (table.schema) {
|
|
3551
|
+
return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
|
|
3552
|
+
}
|
|
3553
|
+
return this.quoteIdentifier(table.name);
|
|
3554
|
+
}
|
|
3555
|
+
compileJoins(ast, ctx) {
|
|
3556
|
+
if (!ast.joins || ast.joins.length === 0) return "";
|
|
3557
|
+
const parts = ast.joins.map((j) => {
|
|
3558
|
+
const table = this.compileFrom(j.table);
|
|
3050
3559
|
const cond = this.compileExpression(j.condition, ctx);
|
|
3051
3560
|
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
3052
|
-
})
|
|
3053
|
-
|
|
3054
|
-
const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
|
|
3055
|
-
const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
|
|
3056
|
-
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(", ") : "";
|
|
3057
|
-
const limit = ast.limit ? ` LIMIT ${ast.limit}` : "";
|
|
3058
|
-
const offset = ast.offset ? ` OFFSET ${ast.offset}` : "";
|
|
3059
|
-
const ctes = ast.ctes && ast.ctes.length > 0 ? (() => {
|
|
3060
|
-
const hasRecursive = ast.ctes.some((cte) => cte.recursive);
|
|
3061
|
-
const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
|
|
3062
|
-
const cteDefs = ast.ctes.map((cte) => {
|
|
3063
|
-
const name = this.quoteIdentifier(cte.name);
|
|
3064
|
-
const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
3065
|
-
const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, "");
|
|
3066
|
-
return `${name}${cols} AS (${query})`;
|
|
3067
|
-
}).join(", ");
|
|
3068
|
-
return prefix + cteDefs + " ";
|
|
3069
|
-
})() : "";
|
|
3070
|
-
return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
|
|
3561
|
+
});
|
|
3562
|
+
return ` ${parts.join(" ")}`;
|
|
3071
3563
|
}
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
const
|
|
3075
|
-
|
|
3076
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
3564
|
+
compileGroupBy(ast) {
|
|
3565
|
+
if (!ast.groupBy || ast.groupBy.length === 0) return "";
|
|
3566
|
+
const cols = ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ");
|
|
3567
|
+
return ` GROUP BY ${cols}`;
|
|
3077
3568
|
}
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3569
|
+
compileHaving(ast, ctx) {
|
|
3570
|
+
if (!ast.having) return "";
|
|
3571
|
+
return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
|
|
3572
|
+
}
|
|
3573
|
+
compileOrderBy(ast) {
|
|
3574
|
+
if (!ast.orderBy || ast.orderBy.length === 0) return "";
|
|
3575
|
+
const parts = ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
|
|
3576
|
+
return ` ORDER BY ${parts}`;
|
|
3577
|
+
}
|
|
3578
|
+
/**
|
|
3579
|
+
* Default LIMIT/OFFSET pagination clause.
|
|
3580
|
+
*/
|
|
3581
|
+
compilePagination(ast, _orderByClause) {
|
|
3582
|
+
const parts = [];
|
|
3583
|
+
if (ast.limit !== void 0) parts.push(`LIMIT ${ast.limit}`);
|
|
3584
|
+
if (ast.offset !== void 0) parts.push(`OFFSET ${ast.offset}`);
|
|
3585
|
+
return parts.length ? ` ${parts.join(" ")}` : "";
|
|
3586
|
+
}
|
|
3587
|
+
compileCtes(ast, ctx) {
|
|
3588
|
+
if (!ast.ctes || ast.ctes.length === 0) return "";
|
|
3589
|
+
const hasRecursive = ast.ctes.some((cte) => cte.recursive);
|
|
3590
|
+
const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
|
|
3591
|
+
const cteDefs = ast.ctes.map((cte) => {
|
|
3592
|
+
const name = this.quoteIdentifier(cte.name);
|
|
3593
|
+
const cols = cte.columns && cte.columns.length ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
3594
|
+
const query = this.stripTrailingSemicolon(this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx));
|
|
3595
|
+
return `${name}${cols} AS (${query})`;
|
|
3085
3596
|
}).join(", ");
|
|
3086
|
-
|
|
3087
|
-
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
3597
|
+
return `${prefix}${cteDefs} `;
|
|
3088
3598
|
}
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3599
|
+
stripTrailingSemicolon(sql) {
|
|
3600
|
+
return sql.trim().replace(/;$/, "");
|
|
3601
|
+
}
|
|
3602
|
+
wrapSetOperand(sql) {
|
|
3603
|
+
const trimmed = this.stripTrailingSemicolon(sql);
|
|
3604
|
+
return `(${trimmed})`;
|
|
3605
|
+
}
|
|
3606
|
+
};
|
|
3607
|
+
|
|
3608
|
+
// src/core/dialect/mysql/index.ts
|
|
3609
|
+
var MySqlDialect = class extends SqlDialectBase {
|
|
3610
|
+
/**
|
|
3611
|
+
* Creates a new MySqlDialect instance
|
|
3612
|
+
*/
|
|
3613
|
+
constructor() {
|
|
3614
|
+
super();
|
|
3615
|
+
}
|
|
3616
|
+
/**
|
|
3617
|
+
* Quotes an identifier using MySQL backtick syntax
|
|
3618
|
+
* @param id - Identifier to quote
|
|
3619
|
+
* @returns Quoted identifier
|
|
3620
|
+
*/
|
|
3621
|
+
quoteIdentifier(id) {
|
|
3622
|
+
return `\`${id}\``;
|
|
3623
|
+
}
|
|
3624
|
+
/**
|
|
3625
|
+
* Compiles JSON path expression using MySQL syntax
|
|
3626
|
+
* @param node - JSON path node
|
|
3627
|
+
* @returns MySQL JSON path expression
|
|
3628
|
+
*/
|
|
3629
|
+
compileJsonPath(node) {
|
|
3630
|
+
const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
3631
|
+
return `${col2}->'${node.path}'`;
|
|
3093
3632
|
}
|
|
3094
3633
|
};
|
|
3095
3634
|
|
|
@@ -3133,6 +3672,43 @@ var SqlServerDialect = class extends Dialect {
|
|
|
3133
3672
|
* @returns SQL Server SQL string
|
|
3134
3673
|
*/
|
|
3135
3674
|
compileSelectAst(ast, ctx) {
|
|
3675
|
+
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
3676
|
+
const ctes = this.compileCtes(ast, ctx);
|
|
3677
|
+
const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
|
|
3678
|
+
const baseSelect = this.compileSelectCore(baseAst, ctx);
|
|
3679
|
+
if (!hasSetOps) {
|
|
3680
|
+
return `${ctes}${baseSelect}`;
|
|
3681
|
+
}
|
|
3682
|
+
const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
|
|
3683
|
+
const orderBy = this.compileOrderBy(ast);
|
|
3684
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
3685
|
+
const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
|
|
3686
|
+
const tail = pagination || orderBy;
|
|
3687
|
+
return `${ctes}${combined}${tail}`;
|
|
3688
|
+
}
|
|
3689
|
+
compileInsertAst(ast, ctx) {
|
|
3690
|
+
const table = this.quoteIdentifier(ast.into.name);
|
|
3691
|
+
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
3692
|
+
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
3693
|
+
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
3694
|
+
}
|
|
3695
|
+
compileUpdateAst(ast, ctx) {
|
|
3696
|
+
const table = this.quoteIdentifier(ast.table.name);
|
|
3697
|
+
const assignments = ast.set.map((assignment) => {
|
|
3698
|
+
const col2 = assignment.column;
|
|
3699
|
+
const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
|
|
3700
|
+
const value = this.compileOperand(assignment.value, ctx);
|
|
3701
|
+
return `${target} = ${value}`;
|
|
3702
|
+
}).join(", ");
|
|
3703
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3704
|
+
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
3705
|
+
}
|
|
3706
|
+
compileDeleteAst(ast, ctx) {
|
|
3707
|
+
const table = this.quoteIdentifier(ast.from.name);
|
|
3708
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3709
|
+
return `DELETE FROM ${table}${whereClause};`;
|
|
3710
|
+
}
|
|
3711
|
+
compileSelectCore(ast, ctx) {
|
|
3136
3712
|
const columns = ast.columns.map((c) => {
|
|
3137
3713
|
let expr = "";
|
|
3138
3714
|
if (c.type === "Function") {
|
|
@@ -3160,51 +3736,47 @@ var SqlServerDialect = class extends Dialect {
|
|
|
3160
3736
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3161
3737
|
const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
|
|
3162
3738
|
const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
|
|
3163
|
-
const orderBy =
|
|
3164
|
-
|
|
3165
|
-
if (
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3739
|
+
const orderBy = this.compileOrderBy(ast);
|
|
3740
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
3741
|
+
if (pagination) {
|
|
3742
|
+
return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${pagination}`;
|
|
3743
|
+
}
|
|
3744
|
+
return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}`;
|
|
3745
|
+
}
|
|
3746
|
+
compileOrderBy(ast) {
|
|
3747
|
+
if (!ast.orderBy || ast.orderBy.length === 0) return "";
|
|
3748
|
+
return " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
|
|
3749
|
+
}
|
|
3750
|
+
compilePagination(ast, orderBy) {
|
|
3751
|
+
const hasLimit = ast.limit !== void 0;
|
|
3752
|
+
const hasOffset = ast.offset !== void 0;
|
|
3753
|
+
if (!hasLimit && !hasOffset) return "";
|
|
3754
|
+
const off = ast.offset ?? 0;
|
|
3755
|
+
const orderClause = orderBy || " ORDER BY (SELECT NULL)";
|
|
3756
|
+
let pagination = `${orderClause} OFFSET ${off} ROWS`;
|
|
3757
|
+
if (hasLimit) {
|
|
3758
|
+
pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
|
|
3173
3759
|
}
|
|
3174
|
-
|
|
3760
|
+
return pagination;
|
|
3761
|
+
}
|
|
3762
|
+
compileCtes(ast, ctx) {
|
|
3763
|
+
if (!ast.ctes || ast.ctes.length === 0) return "";
|
|
3764
|
+
const defs = ast.ctes.map((cte) => {
|
|
3175
3765
|
const name = this.quoteIdentifier(cte.name);
|
|
3176
3766
|
const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
3177
|
-
const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, "");
|
|
3767
|
+
const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, "");
|
|
3178
3768
|
return `${name}${cols} AS (${query})`;
|
|
3179
|
-
}).join(", ") + " " : "";
|
|
3180
|
-
return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy};`;
|
|
3181
|
-
}
|
|
3182
|
-
compileInsertAst(ast, ctx) {
|
|
3183
|
-
const table = this.quoteIdentifier(ast.into.name);
|
|
3184
|
-
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
3185
|
-
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
3186
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
3187
|
-
}
|
|
3188
|
-
compileUpdateAst(ast, ctx) {
|
|
3189
|
-
const table = this.quoteIdentifier(ast.table.name);
|
|
3190
|
-
const assignments = ast.set.map((assignment) => {
|
|
3191
|
-
const col2 = assignment.column;
|
|
3192
|
-
const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
|
|
3193
|
-
const value = this.compileOperand(assignment.value, ctx);
|
|
3194
|
-
return `${target} = ${value}`;
|
|
3195
3769
|
}).join(", ");
|
|
3196
|
-
|
|
3197
|
-
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
3770
|
+
return `WITH ${defs} `;
|
|
3198
3771
|
}
|
|
3199
|
-
|
|
3200
|
-
const
|
|
3201
|
-
|
|
3202
|
-
return `DELETE FROM ${table}${whereClause};`;
|
|
3772
|
+
wrapSetOperand(sql) {
|
|
3773
|
+
const trimmed = sql.trim().replace(/;$/, "");
|
|
3774
|
+
return `(${trimmed})`;
|
|
3203
3775
|
}
|
|
3204
3776
|
};
|
|
3205
3777
|
|
|
3206
3778
|
// src/core/dialect/sqlite/index.ts
|
|
3207
|
-
var SqliteDialect = class extends
|
|
3779
|
+
var SqliteDialect = class extends SqlDialectBase {
|
|
3208
3780
|
/**
|
|
3209
3781
|
* Creates a new SqliteDialect instance
|
|
3210
3782
|
*/
|
|
@@ -3228,91 +3800,1143 @@ var SqliteDialect = class extends Dialect {
|
|
|
3228
3800
|
const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
3229
3801
|
return `json_extract(${col2}, '${node.path}')`;
|
|
3230
3802
|
}
|
|
3803
|
+
compileReturning(returning, ctx) {
|
|
3804
|
+
if (!returning || returning.length === 0) return "";
|
|
3805
|
+
const columns = this.formatReturningColumns(returning);
|
|
3806
|
+
return ` RETURNING ${columns}`;
|
|
3807
|
+
}
|
|
3808
|
+
supportsReturning() {
|
|
3809
|
+
return true;
|
|
3810
|
+
}
|
|
3811
|
+
};
|
|
3812
|
+
|
|
3813
|
+
// src/core/dialect/postgres/index.ts
|
|
3814
|
+
var PostgresDialect = class extends SqlDialectBase {
|
|
3231
3815
|
/**
|
|
3232
|
-
*
|
|
3233
|
-
* @param ast - Query AST
|
|
3234
|
-
* @param ctx - Compiler context
|
|
3235
|
-
* @returns SQLite SQL string
|
|
3816
|
+
* Creates a new PostgresDialect instance
|
|
3236
3817
|
*/
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
let expr = "";
|
|
3240
|
-
if (c.type === "Function") {
|
|
3241
|
-
expr = this.compileOperand(c, ctx);
|
|
3242
|
-
} else if (c.type === "Column") {
|
|
3243
|
-
expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
|
|
3244
|
-
} else if (c.type === "ScalarSubquery") {
|
|
3245
|
-
expr = this.compileOperand(c, ctx);
|
|
3246
|
-
} else if (c.type === "CaseExpression") {
|
|
3247
|
-
expr = this.compileOperand(c, ctx);
|
|
3248
|
-
} else if (c.type === "WindowFunction") {
|
|
3249
|
-
expr = this.compileOperand(c, ctx);
|
|
3250
|
-
}
|
|
3251
|
-
if (c.alias) {
|
|
3252
|
-
if (c.alias.includes("(")) return c.alias;
|
|
3253
|
-
return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
|
|
3254
|
-
}
|
|
3255
|
-
return expr;
|
|
3256
|
-
}).join(", ");
|
|
3257
|
-
const distinct = ast.distinct ? "DISTINCT " : "";
|
|
3258
|
-
const from = `${this.quoteIdentifier(ast.from.name)}`;
|
|
3259
|
-
const joins = ast.joins.map((j) => {
|
|
3260
|
-
const table = this.quoteIdentifier(j.table.name);
|
|
3261
|
-
const cond = this.compileExpression(j.condition, ctx);
|
|
3262
|
-
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
3263
|
-
}).join(" ");
|
|
3264
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3265
|
-
const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
|
|
3266
|
-
const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
|
|
3267
|
-
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(", ") : "";
|
|
3268
|
-
const limit = ast.limit ? ` LIMIT ${ast.limit}` : "";
|
|
3269
|
-
const offset = ast.offset ? ` OFFSET ${ast.offset}` : "";
|
|
3270
|
-
const ctes = ast.ctes && ast.ctes.length > 0 ? (() => {
|
|
3271
|
-
const hasRecursive = ast.ctes.some((cte) => cte.recursive);
|
|
3272
|
-
const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
|
|
3273
|
-
const cteDefs = ast.ctes.map((cte) => {
|
|
3274
|
-
const name = this.quoteIdentifier(cte.name);
|
|
3275
|
-
const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
3276
|
-
const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, "");
|
|
3277
|
-
return `${name}${cols} AS (${query})`;
|
|
3278
|
-
}).join(", ");
|
|
3279
|
-
return prefix + cteDefs + " ";
|
|
3280
|
-
})() : "";
|
|
3281
|
-
return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
|
|
3282
|
-
}
|
|
3283
|
-
compileInsertAst(ast, ctx) {
|
|
3284
|
-
const table = this.quoteIdentifier(ast.into.name);
|
|
3285
|
-
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
3286
|
-
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
3287
|
-
const returning = this.compileReturning(ast.returning, ctx);
|
|
3288
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning};`;
|
|
3818
|
+
constructor() {
|
|
3819
|
+
super();
|
|
3289
3820
|
}
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
}).join(", ");
|
|
3298
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3299
|
-
const returning = this.compileReturning(ast.returning, ctx);
|
|
3300
|
-
return `UPDATE ${table} SET ${assignments}${whereClause}${returning};`;
|
|
3821
|
+
/**
|
|
3822
|
+
* Quotes an identifier using PostgreSQL double-quote syntax
|
|
3823
|
+
* @param id - Identifier to quote
|
|
3824
|
+
* @returns Quoted identifier
|
|
3825
|
+
*/
|
|
3826
|
+
quoteIdentifier(id) {
|
|
3827
|
+
return `"${id}"`;
|
|
3301
3828
|
}
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3829
|
+
/**
|
|
3830
|
+
* Compiles JSON path expression using PostgreSQL syntax
|
|
3831
|
+
* @param node - JSON path node
|
|
3832
|
+
* @returns PostgreSQL JSON path expression
|
|
3833
|
+
*/
|
|
3834
|
+
compileJsonPath(node) {
|
|
3835
|
+
const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
3836
|
+
return `${col2}->>'${node.path}'`;
|
|
3307
3837
|
}
|
|
3308
3838
|
compileReturning(returning, ctx) {
|
|
3309
3839
|
if (!returning || returning.length === 0) return "";
|
|
3310
|
-
const columns =
|
|
3311
|
-
const tablePart = column.table ? `${this.quoteIdentifier(column.table)}.` : "";
|
|
3312
|
-
return `${tablePart}${this.quoteIdentifier(column.name)}`;
|
|
3313
|
-
}).join(", ");
|
|
3840
|
+
const columns = this.formatReturningColumns(returning);
|
|
3314
3841
|
return ` RETURNING ${columns}`;
|
|
3315
3842
|
}
|
|
3843
|
+
supportsReturning() {
|
|
3844
|
+
return true;
|
|
3845
|
+
}
|
|
3846
|
+
};
|
|
3847
|
+
|
|
3848
|
+
// src/core/ddl/dialects/base-schema-dialect.ts
|
|
3849
|
+
var BaseSchemaDialect = class {
|
|
3850
|
+
supportsPartialIndexes() {
|
|
3851
|
+
return false;
|
|
3852
|
+
}
|
|
3853
|
+
formatTableName(table) {
|
|
3854
|
+
if (table.schema) {
|
|
3855
|
+
return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
|
|
3856
|
+
}
|
|
3857
|
+
return this.quoteIdentifier(table.name);
|
|
3858
|
+
}
|
|
3859
|
+
renderDefault(value, _column) {
|
|
3860
|
+
return formatLiteral(value, this.name);
|
|
3861
|
+
}
|
|
3862
|
+
renderReference(ref, _table) {
|
|
3863
|
+
const parts = ["REFERENCES", quoteQualified(this, ref.table), `(${this.quoteIdentifier(ref.column)})`];
|
|
3864
|
+
if (ref.onDelete) parts.push("ON DELETE", ref.onDelete);
|
|
3865
|
+
if (ref.onUpdate) parts.push("ON UPDATE", ref.onUpdate);
|
|
3866
|
+
if (ref.deferrable && this.name === "postgres") parts.push("DEFERRABLE INITIALLY DEFERRED");
|
|
3867
|
+
return parts.join(" ");
|
|
3868
|
+
}
|
|
3869
|
+
renderTableOptions(_table) {
|
|
3870
|
+
return void 0;
|
|
3871
|
+
}
|
|
3872
|
+
dropTableSql(table) {
|
|
3873
|
+
return [`DROP TABLE IF EXISTS ${this.formatTableName(table)};`];
|
|
3874
|
+
}
|
|
3875
|
+
warnDropColumn(_table, _column) {
|
|
3876
|
+
return void 0;
|
|
3877
|
+
}
|
|
3878
|
+
};
|
|
3879
|
+
|
|
3880
|
+
// src/core/ddl/dialects/postgres-schema-dialect.ts
|
|
3881
|
+
var PostgresSchemaDialect = class extends BaseSchemaDialect {
|
|
3882
|
+
constructor() {
|
|
3883
|
+
super(...arguments);
|
|
3884
|
+
this.name = "postgres";
|
|
3885
|
+
}
|
|
3886
|
+
quoteIdentifier(id) {
|
|
3887
|
+
return `"${id}"`;
|
|
3888
|
+
}
|
|
3889
|
+
renderColumnType(column) {
|
|
3890
|
+
switch (column.type) {
|
|
3891
|
+
case "INT":
|
|
3892
|
+
case "INTEGER":
|
|
3893
|
+
case "int":
|
|
3894
|
+
case "integer":
|
|
3895
|
+
return "integer";
|
|
3896
|
+
case "BIGINT":
|
|
3897
|
+
case "bigint":
|
|
3898
|
+
return "bigint";
|
|
3899
|
+
case "UUID":
|
|
3900
|
+
case "uuid":
|
|
3901
|
+
return "uuid";
|
|
3902
|
+
case "BOOLEAN":
|
|
3903
|
+
case "boolean":
|
|
3904
|
+
return "boolean";
|
|
3905
|
+
case "JSON":
|
|
3906
|
+
case "json":
|
|
3907
|
+
return "jsonb";
|
|
3908
|
+
case "DECIMAL":
|
|
3909
|
+
case "decimal":
|
|
3910
|
+
return column.args?.length ? `numeric(${column.args[0]}, ${column.args[1] ?? 0})` : "numeric";
|
|
3911
|
+
case "FLOAT":
|
|
3912
|
+
case "float":
|
|
3913
|
+
case "DOUBLE":
|
|
3914
|
+
case "double":
|
|
3915
|
+
return "double precision";
|
|
3916
|
+
case "TIMESTAMPTZ":
|
|
3917
|
+
case "timestamptz":
|
|
3918
|
+
return "timestamptz";
|
|
3919
|
+
case "TIMESTAMP":
|
|
3920
|
+
case "timestamp":
|
|
3921
|
+
return "timestamp";
|
|
3922
|
+
case "DATE":
|
|
3923
|
+
case "date":
|
|
3924
|
+
return "date";
|
|
3925
|
+
case "DATETIME":
|
|
3926
|
+
case "datetime":
|
|
3927
|
+
return "timestamp";
|
|
3928
|
+
case "VARCHAR":
|
|
3929
|
+
case "varchar":
|
|
3930
|
+
return column.args?.length ? `varchar(${column.args[0]})` : "varchar";
|
|
3931
|
+
case "TEXT":
|
|
3932
|
+
case "text":
|
|
3933
|
+
return "text";
|
|
3934
|
+
case "ENUM":
|
|
3935
|
+
case "enum":
|
|
3936
|
+
return "text";
|
|
3937
|
+
default:
|
|
3938
|
+
return String(column.type).toLowerCase();
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
renderAutoIncrement(column) {
|
|
3942
|
+
if (!column.autoIncrement) return void 0;
|
|
3943
|
+
const strategy = column.generated === "always" ? "GENERATED ALWAYS" : "GENERATED BY DEFAULT";
|
|
3944
|
+
return `${strategy} AS IDENTITY`;
|
|
3945
|
+
}
|
|
3946
|
+
renderIndex(table, index) {
|
|
3947
|
+
const name = index.name || deriveIndexName(table, index);
|
|
3948
|
+
const cols = renderIndexColumns(this, index.columns);
|
|
3949
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
3950
|
+
const where = index.where ? ` WHERE ${index.where}` : "";
|
|
3951
|
+
return `CREATE ${unique}INDEX IF NOT EXISTS ${this.quoteIdentifier(name)} ON ${this.formatTableName(table)} (${cols})${where};`;
|
|
3952
|
+
}
|
|
3953
|
+
supportsPartialIndexes() {
|
|
3954
|
+
return true;
|
|
3955
|
+
}
|
|
3956
|
+
dropColumnSql(table, column) {
|
|
3957
|
+
return [`ALTER TABLE ${this.formatTableName(table)} DROP COLUMN ${this.quoteIdentifier(column)};`];
|
|
3958
|
+
}
|
|
3959
|
+
dropIndexSql(table, index) {
|
|
3960
|
+
const qualified = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(index)}` : this.quoteIdentifier(index);
|
|
3961
|
+
return [`DROP INDEX IF EXISTS ${qualified};`];
|
|
3962
|
+
}
|
|
3963
|
+
};
|
|
3964
|
+
|
|
3965
|
+
// src/core/ddl/dialects/mysql-schema-dialect.ts
|
|
3966
|
+
var MySqlSchemaDialect = class extends BaseSchemaDialect {
|
|
3967
|
+
constructor() {
|
|
3968
|
+
super(...arguments);
|
|
3969
|
+
this.name = "mysql";
|
|
3970
|
+
}
|
|
3971
|
+
quoteIdentifier(id) {
|
|
3972
|
+
return `\`${id}\``;
|
|
3973
|
+
}
|
|
3974
|
+
renderColumnType(column) {
|
|
3975
|
+
switch (column.type) {
|
|
3976
|
+
case "INT":
|
|
3977
|
+
case "INTEGER":
|
|
3978
|
+
case "int":
|
|
3979
|
+
case "integer":
|
|
3980
|
+
return "INT";
|
|
3981
|
+
case "BIGINT":
|
|
3982
|
+
case "bigint":
|
|
3983
|
+
return "BIGINT";
|
|
3984
|
+
case "UUID":
|
|
3985
|
+
case "uuid":
|
|
3986
|
+
return "CHAR(36)";
|
|
3987
|
+
case "BOOLEAN":
|
|
3988
|
+
case "boolean":
|
|
3989
|
+
return "TINYINT(1)";
|
|
3990
|
+
case "JSON":
|
|
3991
|
+
case "json":
|
|
3992
|
+
return "JSON";
|
|
3993
|
+
case "DECIMAL":
|
|
3994
|
+
case "decimal":
|
|
3995
|
+
return column.args?.length ? `DECIMAL(${column.args[0]},${column.args[1] ?? 0})` : "DECIMAL";
|
|
3996
|
+
case "FLOAT":
|
|
3997
|
+
case "float":
|
|
3998
|
+
return column.args?.length ? `FLOAT(${column.args[0]})` : "FLOAT";
|
|
3999
|
+
case "DOUBLE":
|
|
4000
|
+
case "double":
|
|
4001
|
+
return "DOUBLE";
|
|
4002
|
+
case "TIMESTAMPTZ":
|
|
4003
|
+
case "timestamptz":
|
|
4004
|
+
return "TIMESTAMP";
|
|
4005
|
+
case "TIMESTAMP":
|
|
4006
|
+
case "timestamp":
|
|
4007
|
+
return "TIMESTAMP";
|
|
4008
|
+
case "DATETIME":
|
|
4009
|
+
case "datetime":
|
|
4010
|
+
return "DATETIME";
|
|
4011
|
+
case "DATE":
|
|
4012
|
+
case "date":
|
|
4013
|
+
return "DATE";
|
|
4014
|
+
case "VARCHAR":
|
|
4015
|
+
case "varchar":
|
|
4016
|
+
return column.args?.length ? `VARCHAR(${column.args[0]})` : "VARCHAR(255)";
|
|
4017
|
+
case "TEXT":
|
|
4018
|
+
case "text":
|
|
4019
|
+
return "TEXT";
|
|
4020
|
+
case "ENUM":
|
|
4021
|
+
case "enum":
|
|
4022
|
+
return column.args && Array.isArray(column.args) && column.args.length ? `ENUM(${column.args.map((v) => `'${escapeLiteral(v)}'`).join(",")})` : "ENUM";
|
|
4023
|
+
default:
|
|
4024
|
+
return String(column.type).toUpperCase();
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
renderDefault(value) {
|
|
4028
|
+
return formatLiteral(value, this.name);
|
|
4029
|
+
}
|
|
4030
|
+
renderAutoIncrement(column) {
|
|
4031
|
+
return column.autoIncrement ? "AUTO_INCREMENT" : void 0;
|
|
4032
|
+
}
|
|
4033
|
+
renderIndex(table, index) {
|
|
4034
|
+
if (index.where) {
|
|
4035
|
+
throw new Error("MySQL does not support partial/filtered indexes");
|
|
4036
|
+
}
|
|
4037
|
+
const name = index.name || deriveIndexName(table, index);
|
|
4038
|
+
const cols = renderIndexColumns(this, index.columns);
|
|
4039
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
4040
|
+
return `CREATE ${unique}INDEX ${this.quoteIdentifier(name)} ON ${this.formatTableName(table)} (${cols});`;
|
|
4041
|
+
}
|
|
4042
|
+
renderTableOptions(table) {
|
|
4043
|
+
const parts = [];
|
|
4044
|
+
if (table.engine) parts.push(`ENGINE=${table.engine}`);
|
|
4045
|
+
if (table.charset) parts.push(`DEFAULT CHARSET=${table.charset}`);
|
|
4046
|
+
if (table.collation) parts.push(`COLLATE=${table.collation}`);
|
|
4047
|
+
return parts.length ? parts.join(" ") : void 0;
|
|
4048
|
+
}
|
|
4049
|
+
dropColumnSql(table, column) {
|
|
4050
|
+
return [`ALTER TABLE ${this.formatTableName(table)} DROP COLUMN ${this.quoteIdentifier(column)};`];
|
|
4051
|
+
}
|
|
4052
|
+
dropIndexSql(table, index) {
|
|
4053
|
+
return [`DROP INDEX ${this.quoteIdentifier(index)} ON ${this.formatTableName(table)};`];
|
|
4054
|
+
}
|
|
4055
|
+
};
|
|
4056
|
+
|
|
4057
|
+
// src/core/ddl/dialects/sqlite-schema-dialect.ts
|
|
4058
|
+
var SQLiteSchemaDialect = class extends BaseSchemaDialect {
|
|
4059
|
+
constructor() {
|
|
4060
|
+
super(...arguments);
|
|
4061
|
+
this.name = "sqlite";
|
|
4062
|
+
}
|
|
4063
|
+
quoteIdentifier(id) {
|
|
4064
|
+
return `"${id}"`;
|
|
4065
|
+
}
|
|
4066
|
+
renderColumnType(column) {
|
|
4067
|
+
switch (column.type) {
|
|
4068
|
+
case "INT":
|
|
4069
|
+
case "INTEGER":
|
|
4070
|
+
case "int":
|
|
4071
|
+
case "integer":
|
|
4072
|
+
case "BIGINT":
|
|
4073
|
+
case "bigint":
|
|
4074
|
+
return "INTEGER";
|
|
4075
|
+
case "BOOLEAN":
|
|
4076
|
+
case "boolean":
|
|
4077
|
+
return "INTEGER";
|
|
4078
|
+
case "DECIMAL":
|
|
4079
|
+
case "decimal":
|
|
4080
|
+
case "FLOAT":
|
|
4081
|
+
case "float":
|
|
4082
|
+
case "DOUBLE":
|
|
4083
|
+
case "double":
|
|
4084
|
+
return "REAL";
|
|
4085
|
+
case "DATE":
|
|
4086
|
+
case "date":
|
|
4087
|
+
case "DATETIME":
|
|
4088
|
+
case "datetime":
|
|
4089
|
+
case "TIMESTAMP":
|
|
4090
|
+
case "timestamp":
|
|
4091
|
+
case "TIMESTAMPTZ":
|
|
4092
|
+
case "timestamptz":
|
|
4093
|
+
return "TEXT";
|
|
4094
|
+
case "VARCHAR":
|
|
4095
|
+
case "varchar":
|
|
4096
|
+
case "TEXT":
|
|
4097
|
+
case "text":
|
|
4098
|
+
case "JSON":
|
|
4099
|
+
case "json":
|
|
4100
|
+
case "UUID":
|
|
4101
|
+
case "uuid":
|
|
4102
|
+
return "TEXT";
|
|
4103
|
+
case "ENUM":
|
|
4104
|
+
case "enum":
|
|
4105
|
+
return "TEXT";
|
|
4106
|
+
default:
|
|
4107
|
+
return "TEXT";
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
renderAutoIncrement(column, table) {
|
|
4111
|
+
const pk = resolvePrimaryKey(table);
|
|
4112
|
+
if (column.autoIncrement && pk.length === 1 && pk[0] === column.name) {
|
|
4113
|
+
return "PRIMARY KEY AUTOINCREMENT";
|
|
4114
|
+
}
|
|
4115
|
+
return void 0;
|
|
4116
|
+
}
|
|
4117
|
+
preferInlinePkAutoincrement(column, table, pk) {
|
|
4118
|
+
return !!(column.autoIncrement && pk.length === 1 && pk[0] === column.name);
|
|
4119
|
+
}
|
|
4120
|
+
renderDefault(value) {
|
|
4121
|
+
return formatLiteral(value, this.name);
|
|
4122
|
+
}
|
|
4123
|
+
renderIndex(table, index) {
|
|
4124
|
+
if (index.where) {
|
|
4125
|
+
throw new Error("SQLite does not support partial/filtered indexes");
|
|
4126
|
+
}
|
|
4127
|
+
const name = index.name || deriveIndexName(table, index);
|
|
4128
|
+
const cols = renderIndexColumns(this, index.columns);
|
|
4129
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
4130
|
+
return `CREATE ${unique}INDEX IF NOT EXISTS ${this.quoteIdentifier(name)} ON ${this.formatTableName(table)} (${cols});`;
|
|
4131
|
+
}
|
|
4132
|
+
dropColumnSql(_table, _column) {
|
|
4133
|
+
return [];
|
|
4134
|
+
}
|
|
4135
|
+
dropIndexSql(_table, index) {
|
|
4136
|
+
return [`DROP INDEX IF EXISTS ${this.quoteIdentifier(index)};`];
|
|
4137
|
+
}
|
|
4138
|
+
warnDropColumn(table, column) {
|
|
4139
|
+
const key = table.schema ? `${table.schema}.${table.name}` : table.name;
|
|
4140
|
+
return `Dropping columns on SQLite requires table rebuild (column ${column} on ${key}).`;
|
|
4141
|
+
}
|
|
4142
|
+
};
|
|
4143
|
+
|
|
4144
|
+
// src/core/ddl/dialects/mssql-schema-dialect.ts
|
|
4145
|
+
var MSSqlSchemaDialect = class extends BaseSchemaDialect {
|
|
4146
|
+
constructor() {
|
|
4147
|
+
super(...arguments);
|
|
4148
|
+
this.name = "mssql";
|
|
4149
|
+
}
|
|
4150
|
+
quoteIdentifier(id) {
|
|
4151
|
+
return `[${id.replace(/]/g, "]]")}]`;
|
|
4152
|
+
}
|
|
4153
|
+
renderColumnType(column) {
|
|
4154
|
+
switch (column.type) {
|
|
4155
|
+
case "INT":
|
|
4156
|
+
case "INTEGER":
|
|
4157
|
+
case "int":
|
|
4158
|
+
case "integer":
|
|
4159
|
+
return "INT";
|
|
4160
|
+
case "BIGINT":
|
|
4161
|
+
case "bigint":
|
|
4162
|
+
return "BIGINT";
|
|
4163
|
+
case "UUID":
|
|
4164
|
+
case "uuid":
|
|
4165
|
+
return "UNIQUEIDENTIFIER";
|
|
4166
|
+
case "BOOLEAN":
|
|
4167
|
+
case "boolean":
|
|
4168
|
+
return "BIT";
|
|
4169
|
+
case "JSON":
|
|
4170
|
+
case "json":
|
|
4171
|
+
return "NVARCHAR(MAX)";
|
|
4172
|
+
case "DECIMAL":
|
|
4173
|
+
case "decimal":
|
|
4174
|
+
return column.args?.length ? `DECIMAL(${column.args[0]},${column.args[1] ?? 0})` : "DECIMAL(18,0)";
|
|
4175
|
+
case "FLOAT":
|
|
4176
|
+
case "float":
|
|
4177
|
+
case "DOUBLE":
|
|
4178
|
+
case "double":
|
|
4179
|
+
return "FLOAT";
|
|
4180
|
+
case "TIMESTAMPTZ":
|
|
4181
|
+
case "timestamptz":
|
|
4182
|
+
case "TIMESTAMP":
|
|
4183
|
+
case "timestamp":
|
|
4184
|
+
case "DATETIME":
|
|
4185
|
+
case "datetime":
|
|
4186
|
+
return "DATETIME2";
|
|
4187
|
+
case "DATE":
|
|
4188
|
+
case "date":
|
|
4189
|
+
return "DATE";
|
|
4190
|
+
case "VARCHAR":
|
|
4191
|
+
case "varchar":
|
|
4192
|
+
return column.args?.length ? `NVARCHAR(${column.args[0]})` : "NVARCHAR(255)";
|
|
4193
|
+
case "TEXT":
|
|
4194
|
+
case "text":
|
|
4195
|
+
return "NVARCHAR(MAX)";
|
|
4196
|
+
case "ENUM":
|
|
4197
|
+
case "enum":
|
|
4198
|
+
return "NVARCHAR(255)";
|
|
4199
|
+
default:
|
|
4200
|
+
return String(column.type).toUpperCase();
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
renderDefault(value) {
|
|
4204
|
+
return formatLiteral(value, this.name);
|
|
4205
|
+
}
|
|
4206
|
+
renderAutoIncrement(column) {
|
|
4207
|
+
return column.autoIncrement ? "IDENTITY(1,1)" : void 0;
|
|
4208
|
+
}
|
|
4209
|
+
renderIndex(table, index) {
|
|
4210
|
+
const name = index.name || deriveIndexName(table, index);
|
|
4211
|
+
const cols = renderIndexColumns(this, index.columns);
|
|
4212
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
4213
|
+
const where = index.where ? ` WHERE ${index.where}` : "";
|
|
4214
|
+
return `CREATE ${unique}INDEX ${this.quoteIdentifier(name)} ON ${this.formatTableName(table)} (${cols})${where};`;
|
|
4215
|
+
}
|
|
4216
|
+
supportsPartialIndexes() {
|
|
4217
|
+
return true;
|
|
4218
|
+
}
|
|
4219
|
+
dropColumnSql(table, column) {
|
|
4220
|
+
return [`ALTER TABLE ${this.formatTableName(table)} DROP COLUMN ${this.quoteIdentifier(column)};`];
|
|
4221
|
+
}
|
|
4222
|
+
dropIndexSql(table, index) {
|
|
4223
|
+
return [`DROP INDEX ${this.quoteIdentifier(index)} ON ${this.formatTableName(table)};`];
|
|
4224
|
+
}
|
|
4225
|
+
};
|
|
4226
|
+
|
|
4227
|
+
// src/core/ddl/schema-generator.ts
|
|
4228
|
+
var escapeLiteral = (value) => value.replace(/'/g, "''");
|
|
4229
|
+
var isRawDefault = (value) => {
|
|
4230
|
+
return !!value && typeof value === "object" && "raw" in value && typeof value.raw === "string";
|
|
4231
|
+
};
|
|
4232
|
+
var formatLiteral = (value, dialect) => {
|
|
4233
|
+
if (isRawDefault(value)) return value.raw;
|
|
4234
|
+
if (value === null) return "NULL";
|
|
4235
|
+
if (typeof value === "number") return Number.isFinite(value) ? String(value) : "NULL";
|
|
4236
|
+
if (typeof value === "boolean") {
|
|
4237
|
+
if (dialect === "mysql" || dialect === "sqlite" || dialect === "mssql") {
|
|
4238
|
+
return value ? "1" : "0";
|
|
4239
|
+
}
|
|
4240
|
+
return value ? "TRUE" : "FALSE";
|
|
4241
|
+
}
|
|
4242
|
+
if (value instanceof Date) return `'${escapeLiteral(value.toISOString())}'`;
|
|
4243
|
+
if (typeof value === "string") return `'${escapeLiteral(value)}'`;
|
|
4244
|
+
return `'${escapeLiteral(JSON.stringify(value))}'`;
|
|
4245
|
+
};
|
|
4246
|
+
var resolvePrimaryKey = (table) => {
|
|
4247
|
+
if (table.primaryKey && table.primaryKey.length > 0) {
|
|
4248
|
+
return table.primaryKey;
|
|
4249
|
+
}
|
|
4250
|
+
const cols = Object.values(table.columns);
|
|
4251
|
+
return cols.filter((c) => c.primary).map((c) => c.name);
|
|
4252
|
+
};
|
|
4253
|
+
var quoteQualified = (dialect, identifier) => {
|
|
4254
|
+
if (identifier.includes(".")) {
|
|
4255
|
+
return identifier.split(".").map((part) => dialect.quoteIdentifier(part)).join(".");
|
|
4256
|
+
}
|
|
4257
|
+
return dialect.quoteIdentifier(identifier);
|
|
4258
|
+
};
|
|
4259
|
+
var renderIndexColumns = (dialect, columns) => {
|
|
4260
|
+
return columns.map((col2) => {
|
|
4261
|
+
if (typeof col2 === "string") return dialect.quoteIdentifier(col2);
|
|
4262
|
+
const parts = [dialect.quoteIdentifier(col2.column)];
|
|
4263
|
+
if (col2.order) parts.push(col2.order);
|
|
4264
|
+
if (col2.nulls) parts.push(`NULLS ${col2.nulls}`);
|
|
4265
|
+
return parts.join(" ");
|
|
4266
|
+
}).join(", ");
|
|
4267
|
+
};
|
|
4268
|
+
var deriveIndexName = (table, index) => {
|
|
4269
|
+
const base = (index.columns || []).map((col2) => typeof col2 === "string" ? col2 : col2.column).join("_");
|
|
4270
|
+
const suffix = index.unique ? "uniq" : "idx";
|
|
4271
|
+
return `${table.name}_${base}_${suffix}`;
|
|
4272
|
+
};
|
|
4273
|
+
var renderColumnDefinition = (table, col2, dialect, options = {}) => {
|
|
4274
|
+
const parts = [];
|
|
4275
|
+
parts.push(dialect.quoteIdentifier(col2.name));
|
|
4276
|
+
parts.push(dialect.renderColumnType(col2));
|
|
4277
|
+
const autoInc = dialect.renderAutoIncrement(col2, table);
|
|
4278
|
+
if (autoInc) parts.push(autoInc);
|
|
4279
|
+
if (col2.notNull) parts.push("NOT NULL");
|
|
4280
|
+
if (col2.unique) parts.push("UNIQUE");
|
|
4281
|
+
if (col2.default !== void 0) {
|
|
4282
|
+
parts.push(`DEFAULT ${dialect.renderDefault(col2.default, col2)}`);
|
|
4283
|
+
}
|
|
4284
|
+
if (options.includePrimary && col2.primary) {
|
|
4285
|
+
parts.push("PRIMARY KEY");
|
|
4286
|
+
}
|
|
4287
|
+
if (col2.check) {
|
|
4288
|
+
parts.push(`CHECK (${col2.check})`);
|
|
4289
|
+
}
|
|
4290
|
+
if (col2.references) {
|
|
4291
|
+
parts.push(dialect.renderReference(col2.references, table));
|
|
4292
|
+
}
|
|
4293
|
+
return { sql: parts.join(" "), inlinePrimary: !!(options.includePrimary && col2.primary) };
|
|
4294
|
+
};
|
|
4295
|
+
var generateCreateTableSql = (table, dialect) => {
|
|
4296
|
+
const pk = resolvePrimaryKey(table);
|
|
4297
|
+
const inlinePkColumns = /* @__PURE__ */ new Set();
|
|
4298
|
+
const columnLines = Object.values(table.columns).map((col2) => {
|
|
4299
|
+
const includePk = dialect.preferInlinePkAutoincrement?.(col2, table, pk) && pk.includes(col2.name);
|
|
4300
|
+
if (includePk) {
|
|
4301
|
+
inlinePkColumns.add(col2.name);
|
|
4302
|
+
}
|
|
4303
|
+
return renderColumnDefinition(table, col2, dialect, { includePrimary: includePk }).sql;
|
|
4304
|
+
});
|
|
4305
|
+
const constraintLines = [];
|
|
4306
|
+
if (pk.length > 0 && !(pk.length === 1 && inlinePkColumns.has(pk[0]))) {
|
|
4307
|
+
const cols = pk.map((c) => dialect.quoteIdentifier(c)).join(", ");
|
|
4308
|
+
constraintLines.push(`PRIMARY KEY (${cols})`);
|
|
4309
|
+
}
|
|
4310
|
+
if (table.checks) {
|
|
4311
|
+
table.checks.forEach((check) => {
|
|
4312
|
+
const name = check.name ? `${dialect.quoteIdentifier(check.name)} ` : "";
|
|
4313
|
+
constraintLines.push(`CONSTRAINT ${name}CHECK (${check.expression})`);
|
|
4314
|
+
});
|
|
4315
|
+
}
|
|
4316
|
+
const allLines = [...columnLines, ...constraintLines];
|
|
4317
|
+
const body = allLines.map((line) => ` ${line}`).join(",\n");
|
|
4318
|
+
const tableOptions = dialect.renderTableOptions(table);
|
|
4319
|
+
const tableSql = `CREATE TABLE ${dialect.formatTableName(table)} (
|
|
4320
|
+
${body}
|
|
4321
|
+
)${tableOptions ? " " + tableOptions : ""};`;
|
|
4322
|
+
const indexSql = [];
|
|
4323
|
+
if (table.indexes && table.indexes.length > 0) {
|
|
4324
|
+
for (const idx of table.indexes) {
|
|
4325
|
+
if (idx.where && !dialect.supportsPartialIndexes()) {
|
|
4326
|
+
throw new Error(`Dialect ${dialect.name} does not support partial/filtered indexes (${idx.name || idx.columns.join("_")}).`);
|
|
4327
|
+
}
|
|
4328
|
+
indexSql.push(dialect.renderIndex(table, idx));
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
return { tableSql, indexSql };
|
|
4332
|
+
};
|
|
4333
|
+
var generateSchemaSql = (tables, dialect) => {
|
|
4334
|
+
const ordered = orderTablesByDependencies(tables);
|
|
4335
|
+
const statements = [];
|
|
4336
|
+
ordered.forEach((table) => {
|
|
4337
|
+
const { tableSql, indexSql } = generateCreateTableSql(table, dialect);
|
|
4338
|
+
statements.push(tableSql, ...indexSql);
|
|
4339
|
+
});
|
|
4340
|
+
return statements;
|
|
4341
|
+
};
|
|
4342
|
+
var orderTablesByDependencies = (tables) => {
|
|
4343
|
+
const map = /* @__PURE__ */ new Map();
|
|
4344
|
+
tables.forEach((t) => map.set(t.name, t));
|
|
4345
|
+
const deps = /* @__PURE__ */ new Map();
|
|
4346
|
+
for (const table of tables) {
|
|
4347
|
+
const refTables = /* @__PURE__ */ new Set();
|
|
4348
|
+
Object.values(table.columns).forEach((col2) => {
|
|
4349
|
+
if (col2.references?.table) {
|
|
4350
|
+
refTables.add(col2.references.table);
|
|
4351
|
+
}
|
|
4352
|
+
});
|
|
4353
|
+
deps.set(table.name, refTables);
|
|
4354
|
+
}
|
|
4355
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4356
|
+
const ordered = [];
|
|
4357
|
+
const visit = (name, stack) => {
|
|
4358
|
+
if (visited.has(name)) return;
|
|
4359
|
+
const table = map.get(name);
|
|
4360
|
+
if (!table) return;
|
|
4361
|
+
if (stack.has(name)) {
|
|
4362
|
+
ordered.push(table);
|
|
4363
|
+
visited.add(name);
|
|
4364
|
+
return;
|
|
4365
|
+
}
|
|
4366
|
+
stack.add(name);
|
|
4367
|
+
for (const dep of deps.get(name) || []) {
|
|
4368
|
+
visit(dep, stack);
|
|
4369
|
+
}
|
|
4370
|
+
stack.delete(name);
|
|
4371
|
+
visited.add(name);
|
|
4372
|
+
ordered.push(table);
|
|
4373
|
+
};
|
|
4374
|
+
tables.forEach((t) => visit(t.name, /* @__PURE__ */ new Set()));
|
|
4375
|
+
return ordered;
|
|
4376
|
+
};
|
|
4377
|
+
|
|
4378
|
+
// src/core/ddl/schema-diff.ts
|
|
4379
|
+
var tableKey = (name, schema) => schema ? `${schema}.${name}` : name;
|
|
4380
|
+
var mapTables = (schema) => {
|
|
4381
|
+
const map = /* @__PURE__ */ new Map();
|
|
4382
|
+
for (const table of schema.tables) {
|
|
4383
|
+
map.set(tableKey(table.name, table.schema), table);
|
|
4384
|
+
}
|
|
4385
|
+
return map;
|
|
4386
|
+
};
|
|
4387
|
+
var buildAddColumnSql = (table, colName, dialect) => {
|
|
4388
|
+
const column = table.columns[colName];
|
|
4389
|
+
const rendered = renderColumnDefinition(table, column, dialect);
|
|
4390
|
+
return `ALTER TABLE ${dialect.formatTableName(table)} ADD ${rendered.sql};`;
|
|
4391
|
+
};
|
|
4392
|
+
var diffSchema = (expectedTables, actualSchema, dialect, options = {}) => {
|
|
4393
|
+
const allowDestructive = options.allowDestructive ?? false;
|
|
4394
|
+
const plan = { changes: [], warnings: [] };
|
|
4395
|
+
const actualMap = mapTables(actualSchema);
|
|
4396
|
+
for (const table of expectedTables) {
|
|
4397
|
+
const key = tableKey(table.name, table.schema);
|
|
4398
|
+
const actual = actualMap.get(key);
|
|
4399
|
+
if (!actual) {
|
|
4400
|
+
const { tableSql, indexSql } = generateCreateTableSql(table, dialect);
|
|
4401
|
+
plan.changes.push({
|
|
4402
|
+
kind: "createTable",
|
|
4403
|
+
table: key,
|
|
4404
|
+
description: `Create table ${key}`,
|
|
4405
|
+
statements: [tableSql, ...indexSql],
|
|
4406
|
+
safe: true
|
|
4407
|
+
});
|
|
4408
|
+
continue;
|
|
4409
|
+
}
|
|
4410
|
+
const actualCols = new Map(actual.columns.map((c) => [c.name, c]));
|
|
4411
|
+
for (const colName of Object.keys(table.columns)) {
|
|
4412
|
+
if (!actualCols.has(colName)) {
|
|
4413
|
+
plan.changes.push({
|
|
4414
|
+
kind: "addColumn",
|
|
4415
|
+
table: key,
|
|
4416
|
+
description: `Add column ${colName} to ${key}`,
|
|
4417
|
+
statements: [buildAddColumnSql(table, colName, dialect)],
|
|
4418
|
+
safe: true
|
|
4419
|
+
});
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
for (const colName of actualCols.keys()) {
|
|
4423
|
+
if (!table.columns[colName]) {
|
|
4424
|
+
plan.changes.push({
|
|
4425
|
+
kind: "dropColumn",
|
|
4426
|
+
table: key,
|
|
4427
|
+
description: `Drop column ${colName} from ${key}`,
|
|
4428
|
+
statements: allowDestructive ? dialect.dropColumnSql(actual, colName) : [],
|
|
4429
|
+
safe: false
|
|
4430
|
+
});
|
|
4431
|
+
const warning = dialect.warnDropColumn?.(actual, colName);
|
|
4432
|
+
if (warning) plan.warnings.push(warning);
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
const expectedIndexes = table.indexes ?? [];
|
|
4436
|
+
const actualIndexes = actual.indexes ?? [];
|
|
4437
|
+
const actualIndexMap = new Map(actualIndexes.map((idx) => [idx.name, idx]));
|
|
4438
|
+
for (const idx of expectedIndexes) {
|
|
4439
|
+
const name = idx.name || deriveIndexName(table, idx);
|
|
4440
|
+
if (!actualIndexMap.has(name)) {
|
|
4441
|
+
plan.changes.push({
|
|
4442
|
+
kind: "addIndex",
|
|
4443
|
+
table: key,
|
|
4444
|
+
description: `Create index ${name} on ${key}`,
|
|
4445
|
+
statements: [dialect.renderIndex(table, { ...idx, name })],
|
|
4446
|
+
safe: true
|
|
4447
|
+
});
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
for (const idx of actualIndexes) {
|
|
4451
|
+
if (idx.name && !expectedIndexes.find((expected) => (expected.name || deriveIndexName(table, expected)) === idx.name)) {
|
|
4452
|
+
plan.changes.push({
|
|
4453
|
+
kind: "dropIndex",
|
|
4454
|
+
table: key,
|
|
4455
|
+
description: `Drop index ${idx.name} on ${key}`,
|
|
4456
|
+
statements: allowDestructive ? dialect.dropIndexSql(actual, idx.name) : [],
|
|
4457
|
+
safe: false
|
|
4458
|
+
});
|
|
4459
|
+
}
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
for (const actual of actualSchema.tables) {
|
|
4463
|
+
const key = tableKey(actual.name, actual.schema);
|
|
4464
|
+
if (!expectedTables.find((t) => tableKey(t.name, t.schema) === key)) {
|
|
4465
|
+
plan.changes.push({
|
|
4466
|
+
kind: "dropTable",
|
|
4467
|
+
table: key,
|
|
4468
|
+
description: `Drop table ${key}`,
|
|
4469
|
+
statements: allowDestructive ? dialect.dropTableSql(actual) : [],
|
|
4470
|
+
safe: false
|
|
4471
|
+
});
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4474
|
+
return plan;
|
|
4475
|
+
};
|
|
4476
|
+
var synchronizeSchema = async (expectedTables, actualSchema, dialect, executor, options = {}) => {
|
|
4477
|
+
const plan = diffSchema(expectedTables, actualSchema, dialect, options);
|
|
4478
|
+
if (options.dryRun) return plan;
|
|
4479
|
+
for (const change of plan.changes) {
|
|
4480
|
+
if (!change.statements.length) continue;
|
|
4481
|
+
if (!change.safe && !options.allowDestructive) continue;
|
|
4482
|
+
for (const stmt of change.statements) {
|
|
4483
|
+
if (!stmt.trim()) continue;
|
|
4484
|
+
await executor.executeSql(stmt);
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
return plan;
|
|
4488
|
+
};
|
|
4489
|
+
|
|
4490
|
+
// src/core/ddl/introspect/utils.ts
|
|
4491
|
+
var toRows = (result) => {
|
|
4492
|
+
if (!result) return [];
|
|
4493
|
+
return result.values.map(
|
|
4494
|
+
(row) => result.columns.reduce((acc, col2, idx) => {
|
|
4495
|
+
acc[col2] = row[idx];
|
|
4496
|
+
return acc;
|
|
4497
|
+
}, {})
|
|
4498
|
+
);
|
|
4499
|
+
};
|
|
4500
|
+
var queryRows = async (executor, sql, params = []) => {
|
|
4501
|
+
const [first] = await executor.executeSql(sql, params);
|
|
4502
|
+
return toRows(first);
|
|
4503
|
+
};
|
|
4504
|
+
var shouldIncludeTable = (name, options) => {
|
|
4505
|
+
if (options.includeTables && !options.includeTables.includes(name)) return false;
|
|
4506
|
+
if (options.excludeTables && options.excludeTables.includes(name)) return false;
|
|
4507
|
+
return true;
|
|
4508
|
+
};
|
|
4509
|
+
|
|
4510
|
+
// src/core/ddl/introspect/postgres.ts
|
|
4511
|
+
var postgresIntrospector = {
|
|
4512
|
+
async introspect(executor, options) {
|
|
4513
|
+
const schema = options.schema || "public";
|
|
4514
|
+
const tables = [];
|
|
4515
|
+
const columnRows = await queryRows(
|
|
4516
|
+
executor,
|
|
4517
|
+
`
|
|
4518
|
+
SELECT table_schema, table_name, column_name, data_type, is_nullable, column_default
|
|
4519
|
+
FROM information_schema.columns
|
|
4520
|
+
WHERE table_schema = $1
|
|
4521
|
+
ORDER BY table_name, ordinal_position
|
|
4522
|
+
`,
|
|
4523
|
+
[schema]
|
|
4524
|
+
);
|
|
4525
|
+
const pkRows = await queryRows(
|
|
4526
|
+
executor,
|
|
4527
|
+
`
|
|
4528
|
+
SELECT
|
|
4529
|
+
ns.nspname AS table_schema,
|
|
4530
|
+
tbl.relname AS table_name,
|
|
4531
|
+
array_agg(att.attname ORDER BY arr.idx) AS pk_columns
|
|
4532
|
+
FROM pg_index i
|
|
4533
|
+
JOIN pg_class tbl ON tbl.oid = i.indrelid
|
|
4534
|
+
JOIN pg_namespace ns ON ns.oid = tbl.relnamespace
|
|
4535
|
+
JOIN LATERAL unnest(i.indkey) WITH ORDINALITY AS arr(attnum, idx) ON TRUE
|
|
4536
|
+
LEFT JOIN pg_attribute att ON att.attrelid = tbl.oid AND att.attnum = arr.attnum
|
|
4537
|
+
WHERE i.indisprimary AND ns.nspname = $1
|
|
4538
|
+
GROUP BY ns.nspname, tbl.relname
|
|
4539
|
+
`,
|
|
4540
|
+
[schema]
|
|
4541
|
+
);
|
|
4542
|
+
const pkMap = /* @__PURE__ */ new Map();
|
|
4543
|
+
pkRows.forEach((r) => {
|
|
4544
|
+
pkMap.set(`${r.table_schema}.${r.table_name}`, r.pk_columns || []);
|
|
4545
|
+
});
|
|
4546
|
+
const fkRows = await queryRows(
|
|
4547
|
+
executor,
|
|
4548
|
+
`
|
|
4549
|
+
SELECT
|
|
4550
|
+
tc.table_schema,
|
|
4551
|
+
tc.table_name,
|
|
4552
|
+
kcu.column_name,
|
|
4553
|
+
ccu.table_schema AS foreign_table_schema,
|
|
4554
|
+
ccu.table_name AS foreign_table_name,
|
|
4555
|
+
ccu.column_name AS foreign_column_name,
|
|
4556
|
+
rc.update_rule AS on_update,
|
|
4557
|
+
rc.delete_rule AS on_delete
|
|
4558
|
+
FROM information_schema.table_constraints AS tc
|
|
4559
|
+
JOIN information_schema.key_column_usage AS kcu
|
|
4560
|
+
ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
|
4561
|
+
JOIN information_schema.constraint_column_usage AS ccu
|
|
4562
|
+
ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema
|
|
4563
|
+
JOIN information_schema.referential_constraints rc
|
|
4564
|
+
ON rc.constraint_name = tc.constraint_name AND rc.constraint_schema = tc.table_schema
|
|
4565
|
+
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = $1
|
|
4566
|
+
`,
|
|
4567
|
+
[schema]
|
|
4568
|
+
);
|
|
4569
|
+
const fkMap = /* @__PURE__ */ new Map();
|
|
4570
|
+
fkRows.forEach((r) => {
|
|
4571
|
+
const key = `${r.table_schema}.${r.table_name}.${r.column_name}`;
|
|
4572
|
+
fkMap.set(key, [{
|
|
4573
|
+
table: `${r.foreign_table_schema}.${r.foreign_table_name}`,
|
|
4574
|
+
column: r.foreign_column_name,
|
|
4575
|
+
onDelete: r.on_delete?.toUpperCase(),
|
|
4576
|
+
onUpdate: r.on_update?.toUpperCase()
|
|
4577
|
+
}]);
|
|
4578
|
+
});
|
|
4579
|
+
const indexRows = await queryRows(
|
|
4580
|
+
executor,
|
|
4581
|
+
`
|
|
4582
|
+
SELECT
|
|
4583
|
+
ns.nspname AS table_schema,
|
|
4584
|
+
tbl.relname AS table_name,
|
|
4585
|
+
idx.relname AS index_name,
|
|
4586
|
+
i.indisunique AS is_unique,
|
|
4587
|
+
pg_get_expr(i.indpred, i.indrelid) AS predicate,
|
|
4588
|
+
array_agg(att.attname ORDER BY arr.idx) AS column_names
|
|
4589
|
+
FROM pg_index i
|
|
4590
|
+
JOIN pg_class tbl ON tbl.oid = i.indrelid
|
|
4591
|
+
JOIN pg_namespace ns ON ns.oid = tbl.relnamespace
|
|
4592
|
+
JOIN pg_class idx ON idx.oid = i.indexrelid
|
|
4593
|
+
JOIN LATERAL unnest(i.indkey) WITH ORDINALITY AS arr(attnum, idx) ON TRUE
|
|
4594
|
+
LEFT JOIN pg_attribute att ON att.attrelid = tbl.oid AND att.attnum = arr.attnum
|
|
4595
|
+
WHERE ns.nspname = $1 AND NOT i.indisprimary
|
|
4596
|
+
GROUP BY ns.nspname, tbl.relname, idx.relname, i.indisunique, i.indpred
|
|
4597
|
+
`,
|
|
4598
|
+
[schema]
|
|
4599
|
+
);
|
|
4600
|
+
const tablesByKey = /* @__PURE__ */ new Map();
|
|
4601
|
+
columnRows.forEach((r) => {
|
|
4602
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4603
|
+
if (!shouldIncludeTable(r.table_name, options)) {
|
|
4604
|
+
return;
|
|
4605
|
+
}
|
|
4606
|
+
if (!tablesByKey.has(key)) {
|
|
4607
|
+
tablesByKey.set(key, {
|
|
4608
|
+
name: r.table_name,
|
|
4609
|
+
schema: r.table_schema,
|
|
4610
|
+
columns: [],
|
|
4611
|
+
primaryKey: pkMap.get(key) || [],
|
|
4612
|
+
indexes: []
|
|
4613
|
+
});
|
|
4614
|
+
}
|
|
4615
|
+
const cols = tablesByKey.get(key);
|
|
4616
|
+
const fk = fkMap.get(`${r.table_schema}.${r.table_name}.${r.column_name}`)?.[0];
|
|
4617
|
+
const column = {
|
|
4618
|
+
name: r.column_name,
|
|
4619
|
+
type: r.data_type,
|
|
4620
|
+
notNull: r.is_nullable === "NO",
|
|
4621
|
+
default: r.column_default ?? void 0,
|
|
4622
|
+
references: fk ? {
|
|
4623
|
+
table: fk.table,
|
|
4624
|
+
column: fk.column,
|
|
4625
|
+
onDelete: fk.onDelete,
|
|
4626
|
+
onUpdate: fk.onUpdate
|
|
4627
|
+
} : void 0
|
|
4628
|
+
};
|
|
4629
|
+
cols.columns.push(column);
|
|
4630
|
+
});
|
|
4631
|
+
indexRows.forEach((r) => {
|
|
4632
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4633
|
+
const table = tablesByKey.get(key);
|
|
4634
|
+
if (!table) return;
|
|
4635
|
+
const idx = {
|
|
4636
|
+
name: r.index_name,
|
|
4637
|
+
columns: (r.column_names || []).map((c) => ({ column: c })),
|
|
4638
|
+
unique: !!r.is_unique,
|
|
4639
|
+
where: r.predicate || void 0
|
|
4640
|
+
};
|
|
4641
|
+
table.indexes = table.indexes || [];
|
|
4642
|
+
table.indexes.push(idx);
|
|
4643
|
+
});
|
|
4644
|
+
tables.push(...tablesByKey.values());
|
|
4645
|
+
return { tables };
|
|
4646
|
+
}
|
|
4647
|
+
};
|
|
4648
|
+
|
|
4649
|
+
// src/core/ddl/introspect/mysql.ts
|
|
4650
|
+
var mysqlIntrospector = {
|
|
4651
|
+
async introspect(executor, options) {
|
|
4652
|
+
const schema = options.schema;
|
|
4653
|
+
const filterClause = schema ? "table_schema = ?" : "table_schema = database()";
|
|
4654
|
+
const params = schema ? [schema] : [];
|
|
4655
|
+
const columnRows = await queryRows(
|
|
4656
|
+
executor,
|
|
4657
|
+
`
|
|
4658
|
+
SELECT table_schema, table_name, column_name, data_type, is_nullable, column_default, extra
|
|
4659
|
+
FROM information_schema.columns
|
|
4660
|
+
WHERE ${filterClause}
|
|
4661
|
+
ORDER BY table_name, ordinal_position
|
|
4662
|
+
`,
|
|
4663
|
+
params
|
|
4664
|
+
);
|
|
4665
|
+
const pkRows = await queryRows(
|
|
4666
|
+
executor,
|
|
4667
|
+
`
|
|
4668
|
+
SELECT table_schema, table_name, column_name
|
|
4669
|
+
FROM information_schema.key_column_usage
|
|
4670
|
+
WHERE constraint_name = 'PRIMARY' AND ${filterClause}
|
|
4671
|
+
ORDER BY ordinal_position
|
|
4672
|
+
`,
|
|
4673
|
+
params
|
|
4674
|
+
);
|
|
4675
|
+
const pkMap = /* @__PURE__ */ new Map();
|
|
4676
|
+
pkRows.forEach((r) => {
|
|
4677
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4678
|
+
const list = pkMap.get(key) || [];
|
|
4679
|
+
list.push(r.column_name);
|
|
4680
|
+
pkMap.set(key, list);
|
|
4681
|
+
});
|
|
4682
|
+
const indexRows = await queryRows(
|
|
4683
|
+
executor,
|
|
4684
|
+
`
|
|
4685
|
+
SELECT
|
|
4686
|
+
table_schema,
|
|
4687
|
+
table_name,
|
|
4688
|
+
index_name,
|
|
4689
|
+
non_unique,
|
|
4690
|
+
GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols
|
|
4691
|
+
FROM information_schema.statistics
|
|
4692
|
+
WHERE ${filterClause} AND index_name <> 'PRIMARY'
|
|
4693
|
+
GROUP BY table_schema, table_name, index_name, non_unique
|
|
4694
|
+
`,
|
|
4695
|
+
params
|
|
4696
|
+
);
|
|
4697
|
+
const tablesByKey = /* @__PURE__ */ new Map();
|
|
4698
|
+
columnRows.forEach((r) => {
|
|
4699
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4700
|
+
if (!shouldIncludeTable(r.table_name, options)) return;
|
|
4701
|
+
if (!tablesByKey.has(key)) {
|
|
4702
|
+
tablesByKey.set(key, {
|
|
4703
|
+
name: r.table_name,
|
|
4704
|
+
schema: r.table_schema,
|
|
4705
|
+
columns: [],
|
|
4706
|
+
primaryKey: pkMap.get(key) || [],
|
|
4707
|
+
indexes: []
|
|
4708
|
+
});
|
|
4709
|
+
}
|
|
4710
|
+
const cols = tablesByKey.get(key);
|
|
4711
|
+
const column = {
|
|
4712
|
+
name: r.column_name,
|
|
4713
|
+
type: r.data_type,
|
|
4714
|
+
notNull: r.is_nullable === "NO",
|
|
4715
|
+
default: r.column_default ?? void 0,
|
|
4716
|
+
autoIncrement: typeof r.extra === "string" && r.extra.includes("auto_increment")
|
|
4717
|
+
};
|
|
4718
|
+
cols.columns.push(column);
|
|
4719
|
+
});
|
|
4720
|
+
indexRows.forEach((r) => {
|
|
4721
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4722
|
+
const table = tablesByKey.get(key);
|
|
4723
|
+
if (!table) return;
|
|
4724
|
+
const cols = (typeof r.cols === "string" ? r.cols.split(",") : []).map((c) => ({ column: c.trim() }));
|
|
4725
|
+
const idx = {
|
|
4726
|
+
name: r.index_name,
|
|
4727
|
+
columns: cols,
|
|
4728
|
+
unique: r.non_unique === 0
|
|
4729
|
+
};
|
|
4730
|
+
table.indexes = table.indexes || [];
|
|
4731
|
+
table.indexes.push(idx);
|
|
4732
|
+
});
|
|
4733
|
+
return { tables: Array.from(tablesByKey.values()) };
|
|
4734
|
+
}
|
|
4735
|
+
};
|
|
4736
|
+
|
|
4737
|
+
// src/core/ddl/introspect/sqlite.ts
|
|
4738
|
+
var escapeSingleQuotes = (name) => name.replace(/'/g, "''");
|
|
4739
|
+
var sqliteIntrospector = {
|
|
4740
|
+
async introspect(executor, options) {
|
|
4741
|
+
const tables = [];
|
|
4742
|
+
const tableRows = await queryRows(
|
|
4743
|
+
executor,
|
|
4744
|
+
`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%';`
|
|
4745
|
+
);
|
|
4746
|
+
for (const row of tableRows) {
|
|
4747
|
+
const name = row.name;
|
|
4748
|
+
if (!shouldIncludeTable(name, options)) continue;
|
|
4749
|
+
const table = { name, columns: [], primaryKey: [], indexes: [] };
|
|
4750
|
+
const cols = await queryRows(executor, `PRAGMA table_info('${escapeSingleQuotes(name)}');`);
|
|
4751
|
+
cols.forEach((c) => {
|
|
4752
|
+
table.columns.push({
|
|
4753
|
+
name: c.name,
|
|
4754
|
+
type: c.type,
|
|
4755
|
+
notNull: c.notnull === 1,
|
|
4756
|
+
default: c.dflt_value ?? void 0,
|
|
4757
|
+
autoIncrement: false
|
|
4758
|
+
});
|
|
4759
|
+
if (c.pk && c.pk > 0) {
|
|
4760
|
+
table.primaryKey = table.primaryKey || [];
|
|
4761
|
+
table.primaryKey.push(c.name);
|
|
4762
|
+
}
|
|
4763
|
+
});
|
|
4764
|
+
const fkRows = await queryRows(executor, `PRAGMA foreign_key_list('${escapeSingleQuotes(name)}');`);
|
|
4765
|
+
fkRows.forEach((fk) => {
|
|
4766
|
+
const col2 = table.columns.find((c) => c.name === fk.from);
|
|
4767
|
+
if (col2) {
|
|
4768
|
+
col2.references = {
|
|
4769
|
+
table: fk.table,
|
|
4770
|
+
column: fk.to,
|
|
4771
|
+
onDelete: fk.on_delete?.toUpperCase(),
|
|
4772
|
+
onUpdate: fk.on_update?.toUpperCase()
|
|
4773
|
+
};
|
|
4774
|
+
}
|
|
4775
|
+
});
|
|
4776
|
+
const idxList = await queryRows(executor, `PRAGMA index_list('${escapeSingleQuotes(name)}');`);
|
|
4777
|
+
for (const idx of idxList) {
|
|
4778
|
+
const idxName = idx.name;
|
|
4779
|
+
const columnsInfo = await queryRows(executor, `PRAGMA index_info('${escapeSingleQuotes(idxName)}');`);
|
|
4780
|
+
const idxEntry = {
|
|
4781
|
+
name: idxName,
|
|
4782
|
+
columns: columnsInfo.map((ci) => ({ column: ci.name })),
|
|
4783
|
+
unique: idx.unique === 1
|
|
4784
|
+
};
|
|
4785
|
+
table.indexes.push(idxEntry);
|
|
4786
|
+
}
|
|
4787
|
+
tables.push(table);
|
|
4788
|
+
}
|
|
4789
|
+
return { tables };
|
|
4790
|
+
}
|
|
4791
|
+
};
|
|
4792
|
+
|
|
4793
|
+
// src/core/ddl/introspect/mssql.ts
|
|
4794
|
+
var mssqlIntrospector = {
|
|
4795
|
+
async introspect(executor, options) {
|
|
4796
|
+
const schema = options.schema;
|
|
4797
|
+
const filterSchema = schema ? "sch.name = @p1" : "1=1";
|
|
4798
|
+
const params = schema ? [schema] : [];
|
|
4799
|
+
const columnRows = await queryRows(
|
|
4800
|
+
executor,
|
|
4801
|
+
`
|
|
4802
|
+
SELECT
|
|
4803
|
+
sch.name AS table_schema,
|
|
4804
|
+
t.name AS table_name,
|
|
4805
|
+
c.name AS column_name,
|
|
4806
|
+
ty.name AS data_type,
|
|
4807
|
+
c.is_nullable,
|
|
4808
|
+
c.is_identity,
|
|
4809
|
+
object_definition(c.default_object_id) AS column_default
|
|
4810
|
+
FROM sys.columns c
|
|
4811
|
+
JOIN sys.tables t ON t.object_id = c.object_id
|
|
4812
|
+
JOIN sys.schemas sch ON sch.schema_id = t.schema_id
|
|
4813
|
+
JOIN sys.types ty ON ty.user_type_id = c.user_type_id
|
|
4814
|
+
WHERE t.is_ms_shipped = 0 AND ${filterSchema}
|
|
4815
|
+
`,
|
|
4816
|
+
params
|
|
4817
|
+
);
|
|
4818
|
+
const pkRows = await queryRows(
|
|
4819
|
+
executor,
|
|
4820
|
+
`
|
|
4821
|
+
SELECT
|
|
4822
|
+
sch.name AS table_schema,
|
|
4823
|
+
t.name AS table_name,
|
|
4824
|
+
c.name AS column_name,
|
|
4825
|
+
ic.key_ordinal
|
|
4826
|
+
FROM sys.indexes i
|
|
4827
|
+
JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
|
|
4828
|
+
JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
|
|
4829
|
+
JOIN sys.tables t ON t.object_id = i.object_id
|
|
4830
|
+
JOIN sys.schemas sch ON sch.schema_id = t.schema_id
|
|
4831
|
+
WHERE i.is_primary_key = 1 AND ${filterSchema}
|
|
4832
|
+
ORDER BY ic.key_ordinal
|
|
4833
|
+
`,
|
|
4834
|
+
params
|
|
4835
|
+
);
|
|
4836
|
+
const pkMap = /* @__PURE__ */ new Map();
|
|
4837
|
+
pkRows.forEach((r) => {
|
|
4838
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4839
|
+
const list = pkMap.get(key) || [];
|
|
4840
|
+
list.push(r.column_name);
|
|
4841
|
+
pkMap.set(key, list);
|
|
4842
|
+
});
|
|
4843
|
+
const indexRows = await queryRows(
|
|
4844
|
+
executor,
|
|
4845
|
+
`
|
|
4846
|
+
SELECT
|
|
4847
|
+
sch.name AS table_schema,
|
|
4848
|
+
t.name AS table_name,
|
|
4849
|
+
i.name AS index_name,
|
|
4850
|
+
i.is_unique,
|
|
4851
|
+
i.has_filter,
|
|
4852
|
+
i.filter_definition
|
|
4853
|
+
FROM sys.indexes i
|
|
4854
|
+
JOIN sys.tables t ON t.object_id = i.object_id
|
|
4855
|
+
JOIN sys.schemas sch ON sch.schema_id = t.schema_id
|
|
4856
|
+
WHERE i.is_primary_key = 0 AND i.is_hypothetical = 0 AND ${filterSchema}
|
|
4857
|
+
`,
|
|
4858
|
+
params
|
|
4859
|
+
);
|
|
4860
|
+
const indexColsRows = await queryRows(
|
|
4861
|
+
executor,
|
|
4862
|
+
`
|
|
4863
|
+
SELECT
|
|
4864
|
+
sch.name AS table_schema,
|
|
4865
|
+
t.name AS table_name,
|
|
4866
|
+
i.name AS index_name,
|
|
4867
|
+
c.name AS column_name,
|
|
4868
|
+
ic.key_ordinal
|
|
4869
|
+
FROM sys.index_columns ic
|
|
4870
|
+
JOIN sys.indexes i ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
4871
|
+
JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
|
|
4872
|
+
JOIN sys.tables t ON t.object_id = i.object_id
|
|
4873
|
+
JOIN sys.schemas sch ON sch.schema_id = t.schema_id
|
|
4874
|
+
WHERE i.is_primary_key = 0 AND ${filterSchema}
|
|
4875
|
+
ORDER BY ic.key_ordinal
|
|
4876
|
+
`,
|
|
4877
|
+
params
|
|
4878
|
+
);
|
|
4879
|
+
const indexColumnsMap = /* @__PURE__ */ new Map();
|
|
4880
|
+
indexColsRows.forEach((r) => {
|
|
4881
|
+
const key = `${r.table_schema}.${r.table_name}.${r.index_name}`;
|
|
4882
|
+
const list = indexColumnsMap.get(key) || [];
|
|
4883
|
+
list.push({ column: r.column_name, order: r.key_ordinal });
|
|
4884
|
+
indexColumnsMap.set(key, list);
|
|
4885
|
+
});
|
|
4886
|
+
const tablesByKey = /* @__PURE__ */ new Map();
|
|
4887
|
+
columnRows.forEach((r) => {
|
|
4888
|
+
if (!shouldIncludeTable(r.table_name, options)) return;
|
|
4889
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4890
|
+
if (!tablesByKey.has(key)) {
|
|
4891
|
+
tablesByKey.set(key, {
|
|
4892
|
+
name: r.table_name,
|
|
4893
|
+
schema: r.table_schema,
|
|
4894
|
+
columns: [],
|
|
4895
|
+
primaryKey: pkMap.get(key) || [],
|
|
4896
|
+
indexes: []
|
|
4897
|
+
});
|
|
4898
|
+
}
|
|
4899
|
+
const t = tablesByKey.get(key);
|
|
4900
|
+
const column = {
|
|
4901
|
+
name: r.column_name,
|
|
4902
|
+
type: r.data_type,
|
|
4903
|
+
notNull: r.is_nullable === false || r.is_nullable === 0,
|
|
4904
|
+
default: r.column_default ?? void 0,
|
|
4905
|
+
autoIncrement: !!r.is_identity
|
|
4906
|
+
};
|
|
4907
|
+
t.columns.push(column);
|
|
4908
|
+
});
|
|
4909
|
+
indexRows.forEach((r) => {
|
|
4910
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4911
|
+
const table = tablesByKey.get(key);
|
|
4912
|
+
if (!table) return;
|
|
4913
|
+
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 }));
|
|
4914
|
+
const idx = {
|
|
4915
|
+
name: r.index_name,
|
|
4916
|
+
columns: cols,
|
|
4917
|
+
unique: !!r.is_unique,
|
|
4918
|
+
where: r.has_filter ? r.filter_definition : void 0
|
|
4919
|
+
};
|
|
4920
|
+
table.indexes = table.indexes || [];
|
|
4921
|
+
table.indexes.push(idx);
|
|
4922
|
+
});
|
|
4923
|
+
return { tables: Array.from(tablesByKey.values()) };
|
|
4924
|
+
}
|
|
4925
|
+
};
|
|
4926
|
+
|
|
4927
|
+
// src/core/ddl/schema-introspect.ts
|
|
4928
|
+
var INTROSPECTORS = {
|
|
4929
|
+
postgres: postgresIntrospector,
|
|
4930
|
+
mysql: mysqlIntrospector,
|
|
4931
|
+
sqlite: sqliteIntrospector,
|
|
4932
|
+
mssql: mssqlIntrospector
|
|
4933
|
+
};
|
|
4934
|
+
var introspectSchema = async (executor, dialect, options = {}) => {
|
|
4935
|
+
const handler = INTROSPECTORS[dialect];
|
|
4936
|
+
if (!handler) {
|
|
4937
|
+
throw new Error(`Unsupported dialect for introspection: ${dialect}`);
|
|
4938
|
+
}
|
|
4939
|
+
return handler.introspect(executor, options);
|
|
3316
4940
|
};
|
|
3317
4941
|
|
|
3318
4942
|
// src/orm/als.ts
|
|
@@ -3982,9 +5606,13 @@ var UnitOfWork = class {
|
|
|
3982
5606
|
async flushInsert(tracked) {
|
|
3983
5607
|
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
3984
5608
|
const payload = this.extractColumns(tracked.table, tracked.entity);
|
|
3985
|
-
|
|
5609
|
+
let builder = new InsertQueryBuilder(tracked.table).values(payload);
|
|
5610
|
+
if (this.dialect.supportsReturning()) {
|
|
5611
|
+
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
5612
|
+
}
|
|
3986
5613
|
const compiled = builder.compile(this.dialect);
|
|
3987
|
-
await this.executeCompiled(compiled);
|
|
5614
|
+
const results = await this.executeCompiled(compiled);
|
|
5615
|
+
this.applyReturningResults(tracked, results);
|
|
3988
5616
|
tracked.status = "managed" /* Managed */;
|
|
3989
5617
|
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
3990
5618
|
tracked.pk = this.getPrimaryKeyValue(tracked);
|
|
@@ -4001,9 +5629,13 @@ var UnitOfWork = class {
|
|
|
4001
5629
|
await this.runHook(tracked.table.hooks?.beforeUpdate, tracked);
|
|
4002
5630
|
const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
|
|
4003
5631
|
if (!pkColumn) return;
|
|
4004
|
-
|
|
5632
|
+
let builder = new UpdateQueryBuilder(tracked.table).set(changes).where(eq(pkColumn, tracked.pk));
|
|
5633
|
+
if (this.dialect.supportsReturning()) {
|
|
5634
|
+
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
5635
|
+
}
|
|
4005
5636
|
const compiled = builder.compile(this.dialect);
|
|
4006
|
-
await this.executeCompiled(compiled);
|
|
5637
|
+
const results = await this.executeCompiled(compiled);
|
|
5638
|
+
this.applyReturningResults(tracked, results);
|
|
4007
5639
|
tracked.status = "managed" /* Managed */;
|
|
4008
5640
|
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
4009
5641
|
this.registerIdentity(tracked);
|
|
@@ -4045,7 +5677,31 @@ var UnitOfWork = class {
|
|
|
4045
5677
|
return payload;
|
|
4046
5678
|
}
|
|
4047
5679
|
async executeCompiled(compiled) {
|
|
4048
|
-
|
|
5680
|
+
return this.executor.executeSql(compiled.sql, compiled.params);
|
|
5681
|
+
}
|
|
5682
|
+
getReturningColumns(table) {
|
|
5683
|
+
return Object.values(table.columns).map((column) => ({
|
|
5684
|
+
type: "Column",
|
|
5685
|
+
table: table.name,
|
|
5686
|
+
name: column.name,
|
|
5687
|
+
alias: column.name
|
|
5688
|
+
}));
|
|
5689
|
+
}
|
|
5690
|
+
applyReturningResults(tracked, results) {
|
|
5691
|
+
if (!this.dialect.supportsReturning()) return;
|
|
5692
|
+
const first = results[0];
|
|
5693
|
+
if (!first || first.values.length === 0) return;
|
|
5694
|
+
const row = first.values[0];
|
|
5695
|
+
for (let i = 0; i < first.columns.length; i++) {
|
|
5696
|
+
const columnName = this.normalizeColumnName(first.columns[i]);
|
|
5697
|
+
if (!(columnName in tracked.table.columns)) continue;
|
|
5698
|
+
tracked.entity[columnName] = row[i];
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
normalizeColumnName(column) {
|
|
5702
|
+
const parts = column.split(".");
|
|
5703
|
+
const candidate = parts[parts.length - 1];
|
|
5704
|
+
return candidate.replace(/^["`[\]]+|["`[\]]+$/g, "");
|
|
4049
5705
|
}
|
|
4050
5706
|
registerIdentity(tracked) {
|
|
4051
5707
|
if (tracked.pk == null) return;
|
|
@@ -4066,22 +5722,46 @@ var UnitOfWork = class {
|
|
|
4066
5722
|
}
|
|
4067
5723
|
};
|
|
4068
5724
|
|
|
5725
|
+
// src/orm/query-logger.ts
|
|
5726
|
+
var createQueryLoggingExecutor = (executor, logger) => {
|
|
5727
|
+
if (!logger) {
|
|
5728
|
+
return executor;
|
|
5729
|
+
}
|
|
5730
|
+
const wrapped = {
|
|
5731
|
+
async executeSql(sql, params) {
|
|
5732
|
+
logger({ sql, params });
|
|
5733
|
+
return executor.executeSql(sql, params);
|
|
5734
|
+
}
|
|
5735
|
+
};
|
|
5736
|
+
if (executor.beginTransaction) {
|
|
5737
|
+
wrapped.beginTransaction = executor.beginTransaction.bind(executor);
|
|
5738
|
+
}
|
|
5739
|
+
if (executor.commitTransaction) {
|
|
5740
|
+
wrapped.commitTransaction = executor.commitTransaction.bind(executor);
|
|
5741
|
+
}
|
|
5742
|
+
if (executor.rollbackTransaction) {
|
|
5743
|
+
wrapped.rollbackTransaction = executor.rollbackTransaction.bind(executor);
|
|
5744
|
+
}
|
|
5745
|
+
return wrapped;
|
|
5746
|
+
};
|
|
5747
|
+
|
|
4069
5748
|
// src/orm/orm-context.ts
|
|
4070
5749
|
var OrmContext = class {
|
|
4071
5750
|
constructor(options) {
|
|
4072
5751
|
this.options = options;
|
|
4073
5752
|
this.identityMap = new IdentityMap();
|
|
4074
5753
|
this.interceptors = [...options.interceptors ?? []];
|
|
5754
|
+
this.executorWithLogging = createQueryLoggingExecutor(options.executor, options.queryLogger);
|
|
4075
5755
|
this.unitOfWork = new UnitOfWork(
|
|
4076
5756
|
options.dialect,
|
|
4077
|
-
|
|
5757
|
+
this.executorWithLogging,
|
|
4078
5758
|
this.identityMap,
|
|
4079
5759
|
() => this
|
|
4080
5760
|
);
|
|
4081
5761
|
this.relationChanges = new RelationChangeProcessor(
|
|
4082
5762
|
this.unitOfWork,
|
|
4083
5763
|
options.dialect,
|
|
4084
|
-
|
|
5764
|
+
this.executorWithLogging
|
|
4085
5765
|
);
|
|
4086
5766
|
this.domainEvents = new DomainEventBus(options.domainEventHandlers);
|
|
4087
5767
|
}
|
|
@@ -4089,7 +5769,7 @@ var OrmContext = class {
|
|
|
4089
5769
|
return this.options.dialect;
|
|
4090
5770
|
}
|
|
4091
5771
|
get executor() {
|
|
4092
|
-
return this.
|
|
5772
|
+
return this.executorWithLogging;
|
|
4093
5773
|
}
|
|
4094
5774
|
get identityBuckets() {
|
|
4095
5775
|
return this.unitOfWork.identityBuckets;
|
|
@@ -4153,15 +5833,21 @@ var OrmContext = class {
|
|
|
4153
5833
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4154
5834
|
0 && (module.exports = {
|
|
4155
5835
|
AsyncLocalStorage,
|
|
5836
|
+
BaseSchemaDialect,
|
|
4156
5837
|
DefaultBelongsToReference,
|
|
4157
5838
|
DefaultHasManyCollection,
|
|
4158
5839
|
DefaultManyToManyCollection,
|
|
4159
5840
|
DeleteQueryBuilder,
|
|
4160
5841
|
EntityStatus,
|
|
4161
5842
|
InsertQueryBuilder,
|
|
5843
|
+
MSSqlSchemaDialect,
|
|
4162
5844
|
MySqlDialect,
|
|
5845
|
+
MySqlSchemaDialect,
|
|
4163
5846
|
OrmContext,
|
|
5847
|
+
PostgresDialect,
|
|
5848
|
+
PostgresSchemaDialect,
|
|
4164
5849
|
RelationKinds,
|
|
5850
|
+
SQLiteSchemaDialect,
|
|
4165
5851
|
SelectQueryBuilder,
|
|
4166
5852
|
SqlServerDialect,
|
|
4167
5853
|
SqliteDialect,
|
|
@@ -4183,15 +5869,22 @@ var OrmContext = class {
|
|
|
4183
5869
|
createLiteral,
|
|
4184
5870
|
defineTable,
|
|
4185
5871
|
denseRank,
|
|
5872
|
+
deriveIndexName,
|
|
5873
|
+
diffSchema,
|
|
4186
5874
|
eq,
|
|
5875
|
+
escapeLiteral,
|
|
4187
5876
|
executeHydrated,
|
|
4188
5877
|
exists,
|
|
4189
5878
|
firstValue,
|
|
5879
|
+
formatLiteral,
|
|
5880
|
+
generateCreateTableSql,
|
|
5881
|
+
generateSchemaSql,
|
|
4190
5882
|
gt,
|
|
4191
5883
|
gte,
|
|
4192
5884
|
hasMany,
|
|
4193
5885
|
hydrateRows,
|
|
4194
5886
|
inList,
|
|
5887
|
+
introspectSchema,
|
|
4195
5888
|
isCaseExpressionNode,
|
|
4196
5889
|
isExpressionSelectionNode,
|
|
4197
5890
|
isFunctionNode,
|
|
@@ -4216,9 +5909,14 @@ var OrmContext = class {
|
|
|
4216
5909
|
notLike,
|
|
4217
5910
|
ntile,
|
|
4218
5911
|
or,
|
|
5912
|
+
quoteQualified,
|
|
4219
5913
|
rank,
|
|
5914
|
+
renderColumnDefinition,
|
|
5915
|
+
renderIndexColumns,
|
|
5916
|
+
resolvePrimaryKey,
|
|
4220
5917
|
rowNumber,
|
|
4221
5918
|
sum,
|
|
5919
|
+
synchronizeSchema,
|
|
4222
5920
|
valueToOperand,
|
|
4223
5921
|
visitExpression,
|
|
4224
5922
|
visitOperand,
|