metal-orm 1.0.45 → 1.0.47

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.
Files changed (63) hide show
  1. package/dist/index.cjs +1092 -323
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +99 -9
  4. package/dist/index.d.ts +99 -9
  5. package/dist/index.js +1087 -323
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/scripts/generate-entities.mjs +36 -9
  9. package/src/codegen/typescript.ts +22 -10
  10. package/src/core/ast/adapters.ts +2 -1
  11. package/src/core/ast/expression-builders.ts +13 -0
  12. package/src/core/ast/expression-nodes.ts +25 -5
  13. package/src/core/ast/expression-visitor.ts +5 -0
  14. package/src/core/ast/query.ts +9 -1
  15. package/src/core/ddl/dialects/base-schema-dialect.ts +2 -1
  16. package/src/core/ddl/dialects/mssql-schema-dialect.ts +10 -23
  17. package/src/core/ddl/dialects/mysql-schema-dialect.ts +10 -24
  18. package/src/core/ddl/dialects/postgres-schema-dialect.ts +10 -23
  19. package/src/core/ddl/dialects/render-reference.test.ts +2 -1
  20. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -23
  21. package/src/core/ddl/introspect/catalogs/index.ts +4 -1
  22. package/src/core/ddl/introspect/catalogs/mssql.ts +126 -0
  23. package/src/core/ddl/introspect/catalogs/mysql.ts +89 -0
  24. package/src/core/ddl/introspect/catalogs/postgres.ts +2 -1
  25. package/src/core/ddl/introspect/catalogs/sqlite.ts +47 -0
  26. package/src/core/ddl/introspect/functions/mssql.ts +84 -0
  27. package/src/core/ddl/introspect/mssql.ts +471 -194
  28. package/src/core/ddl/introspect/mysql.ts +336 -125
  29. package/src/core/ddl/introspect/postgres.ts +45 -5
  30. package/src/core/ddl/introspect/run-select.ts +3 -8
  31. package/src/core/ddl/introspect/sqlite.ts +113 -59
  32. package/src/core/ddl/schema-dialect.ts +2 -1
  33. package/src/core/ddl/schema-diff.ts +2 -1
  34. package/src/core/ddl/schema-generator.ts +2 -1
  35. package/src/core/ddl/schema-types.ts +3 -1
  36. package/src/core/ddl/sql-writing.ts +2 -1
  37. package/src/core/dialect/abstract.ts +12 -1
  38. package/src/core/dialect/mssql/index.ts +4 -10
  39. package/src/core/functions/datetime.ts +2 -1
  40. package/src/core/functions/numeric.ts +2 -1
  41. package/src/core/functions/text.ts +2 -1
  42. package/src/decorators/{column.ts → column-decorator.ts} +4 -1
  43. package/src/decorators/index.ts +1 -1
  44. package/src/index.ts +2 -1
  45. package/src/orm/entity-metadata.ts +2 -1
  46. package/src/orm/lazy-batch.ts +2 -1
  47. package/src/orm/orm-session.ts +2 -1
  48. package/src/query-builder/column-selector.ts +2 -1
  49. package/src/query-builder/delete.ts +2 -1
  50. package/src/query-builder/insert.ts +2 -1
  51. package/src/query-builder/query-ast-service.ts +4 -3
  52. package/src/query-builder/relation-projection-helper.ts +2 -1
  53. package/src/query-builder/relation-service.ts +2 -1
  54. package/src/query-builder/select/predicate-facet.ts +2 -1
  55. package/src/query-builder/select/projection-facet.ts +2 -1
  56. package/src/query-builder/select-helpers.ts +2 -1
  57. package/src/query-builder/select-query-state.ts +2 -0
  58. package/src/query-builder/select.ts +2 -1
  59. package/src/query-builder/update.ts +2 -1
  60. package/src/schema/{column.ts → column-types.ts} +317 -290
  61. package/src/schema/table-guards.ts +1 -1
  62. package/src/schema/table.ts +1 -1
  63. package/src/schema/types.ts +10 -8
@@ -1,194 +1,471 @@
1
- import { SchemaIntrospector, IntrospectOptions } from './types.js';
2
- import { queryRows, shouldIncludeTable } from './utils.js';
3
- import { DatabaseSchema, DatabaseTable, DatabaseIndex, DatabaseColumn } from '../schema-types.js';
4
- import { DbExecutor } from '../../execution/db-executor.js';
5
-
6
- /** Row type for MSSQL column information. */
7
- type MssqlColumnRow = {
8
- table_schema: string;
9
- table_name: string;
10
- column_name: string;
11
- data_type: string;
12
- is_nullable: boolean | number;
13
- is_identity: boolean | number;
14
- column_default: string | null;
15
- };
16
-
17
- /** Row type for MSSQL primary key information. */
18
- type MssqlPrimaryKeyRow = {
19
- table_schema: string;
20
- table_name: string;
21
- column_name: string;
22
- key_ordinal: number;
23
- };
24
-
25
- /** Row type for MSSQL index information. */
26
- type MssqlIndexRow = {
27
- table_schema: string;
28
- table_name: string;
29
- index_name: string;
30
- is_unique: boolean | number;
31
- has_filter: boolean | number;
32
- filter_definition: string | null;
33
- };
34
-
35
- /** Row type for MSSQL index column information. */
36
- type MssqlIndexColumnRow = {
37
- table_schema: string;
38
- table_name: string;
39
- index_name: string;
40
- column_name: string;
41
- key_ordinal: number;
42
- };
43
-
44
- /** MSSQL schema introspector implementation. */
45
- export const mssqlIntrospector: SchemaIntrospector = {
46
- /**
47
- * Introspects the MSSQL database schema.
48
- * @param ctx - The introspection context containing the database executor.
49
- * @param options - Options for introspection, such as schema filter.
50
- * @returns A promise that resolves to the introspected database schema.
51
- */
52
- async introspect(ctx: { executor: DbExecutor }, options: IntrospectOptions): Promise<DatabaseSchema> {
53
- const schema = options.schema;
54
- const filterSchema = schema ? 'sch.name = @p1' : '1=1';
55
- const params = schema ? [schema] : [];
56
-
57
- const columnRows = (await queryRows(
58
- ctx.executor,
59
- `
60
- SELECT
61
- sch.name AS table_schema,
62
- t.name AS table_name,
63
- c.name AS column_name,
64
- ty.name AS data_type,
65
- c.is_nullable,
66
- c.is_identity,
67
- object_definition(c.default_object_id) AS column_default
68
- FROM sys.columns c
69
- JOIN sys.tables t ON t.object_id = c.object_id
70
- JOIN sys.schemas sch ON sch.schema_id = t.schema_id
71
- JOIN sys.types ty ON ty.user_type_id = c.user_type_id
72
- WHERE t.is_ms_shipped = 0 AND ${filterSchema}
73
- `,
74
- params
75
- )) as MssqlColumnRow[];
76
-
77
- const pkRows = (await queryRows(
78
- ctx.executor,
79
- `
80
- SELECT
81
- sch.name AS table_schema,
82
- t.name AS table_name,
83
- c.name AS column_name,
84
- ic.key_ordinal
85
- FROM sys.indexes i
86
- JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
87
- JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
88
- JOIN sys.tables t ON t.object_id = i.object_id
89
- JOIN sys.schemas sch ON sch.schema_id = t.schema_id
90
- WHERE i.is_primary_key = 1 AND ${filterSchema}
91
- ORDER BY ic.key_ordinal
92
- `,
93
- params
94
- )) as MssqlPrimaryKeyRow[];
95
-
96
- const pkMap = new Map<string, string[]>();
97
- pkRows.forEach(r => {
98
- const key = `${r.table_schema}.${r.table_name}`;
99
- const list = pkMap.get(key) || [];
100
- list.push(r.column_name);
101
- pkMap.set(key, list);
102
- });
103
-
104
- const indexRows = (await queryRows(
105
- ctx.executor,
106
- `
107
- SELECT
108
- sch.name AS table_schema,
109
- t.name AS table_name,
110
- i.name AS index_name,
111
- i.is_unique,
112
- i.has_filter,
113
- i.filter_definition
114
- FROM sys.indexes i
115
- JOIN sys.tables t ON t.object_id = i.object_id
116
- JOIN sys.schemas sch ON sch.schema_id = t.schema_id
117
- WHERE i.is_primary_key = 0 AND i.is_hypothetical = 0 AND ${filterSchema}
118
- `,
119
- params
120
- )) as MssqlIndexRow[];
121
-
122
- const indexColsRows = (await queryRows(
123
- ctx.executor,
124
- `
125
- SELECT
126
- sch.name AS table_schema,
127
- t.name AS table_name,
128
- i.name AS index_name,
129
- c.name AS column_name,
130
- ic.key_ordinal
131
- FROM sys.index_columns ic
132
- JOIN sys.indexes i ON i.object_id = ic.object_id AND i.index_id = ic.index_id
133
- JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
134
- JOIN sys.tables t ON t.object_id = i.object_id
135
- JOIN sys.schemas sch ON sch.schema_id = t.schema_id
136
- WHERE i.is_primary_key = 0 AND ${filterSchema}
137
- ORDER BY ic.key_ordinal
138
- `,
139
- params
140
- )) as MssqlIndexColumnRow[];
141
-
142
- const indexColumnsMap = new Map<string, { column: string; order: number }[]>();
143
- indexColsRows.forEach(r => {
144
- const key = `${r.table_schema}.${r.table_name}.${r.index_name}`;
145
- const list = indexColumnsMap.get(key) || [];
146
- list.push({ column: r.column_name, order: r.key_ordinal });
147
- indexColumnsMap.set(key, list);
148
- });
149
-
150
- const tablesByKey = new Map<string, DatabaseTable>();
151
-
152
- columnRows.forEach(r => {
153
- if (!shouldIncludeTable(r.table_name, options)) return;
154
- const key = `${r.table_schema}.${r.table_name}`;
155
- if (!tablesByKey.has(key)) {
156
- tablesByKey.set(key, {
157
- name: r.table_name,
158
- schema: r.table_schema,
159
- columns: [],
160
- primaryKey: pkMap.get(key) || [],
161
- indexes: []
162
- });
163
- }
164
- const t = tablesByKey.get(key)!;
165
- const column: DatabaseColumn = {
166
- name: r.column_name,
167
- type: r.data_type,
168
- notNull: r.is_nullable === false || r.is_nullable === 0,
169
- default: r.column_default ?? undefined,
170
- autoIncrement: !!r.is_identity
171
- };
172
- t.columns.push(column);
173
- });
174
-
175
- indexRows.forEach(r => {
176
- const key = `${r.table_schema}.${r.table_name}`;
177
- const table = tablesByKey.get(key);
178
- if (!table) return;
179
- const cols = (indexColumnsMap.get(`${r.table_schema}.${r.table_name}.${r.index_name}`) || [])
180
- .sort((a, b) => a.order - b.order)
181
- .map(c => ({ column: c.column }));
182
- const idx: DatabaseIndex = {
183
- name: r.index_name,
184
- columns: cols,
185
- unique: !!r.is_unique,
186
- where: r.has_filter ? r.filter_definition : undefined
187
- };
188
- table.indexes = table.indexes || [];
189
- table.indexes.push(idx);
190
- });
191
-
192
- return { tables: Array.from(tablesByKey.values()) };
193
- }
194
- };
1
+ import type { ReferentialAction } from '../../../schema/column-types.js';
2
+ import { SchemaIntrospector, IntrospectOptions } from './types.js';
3
+ import { shouldIncludeTable } from './utils.js';
4
+ import { DatabaseSchema, DatabaseTable, DatabaseIndex, DatabaseColumn } from '../schema-types.js';
5
+ import type { IntrospectContext } from './context.js';
6
+ import { runSelectNode } from './run-select.js';
7
+ import type { SelectQueryNode, TableNode } from '../../ast/query.js';
8
+ import type { ColumnNode, ExpressionNode, FunctionNode } from '../../ast/expression-nodes.js';
9
+ import type { JoinNode } from '../../ast/join.js';
10
+ import { eq, and } from '../../ast/expression-builders.js';
11
+ import type { TableDef } from '../../../schema/table.js';
12
+ import {
13
+ SysColumns,
14
+ SysTables,
15
+ SysSchemas,
16
+ SysTypes,
17
+ SysIndexes,
18
+ SysIndexColumns,
19
+ SysForeignKeys,
20
+ SysForeignKeyColumns
21
+ } from './catalogs/mssql.js';
22
+ import { buildMssqlDataType, objectDefinition } from './functions/mssql.js';
23
+
24
+ type MssqlColumnRow = {
25
+ table_schema: string;
26
+ table_name: string;
27
+ column_name: string;
28
+ data_type: string;
29
+ is_nullable: boolean | number;
30
+ is_identity: boolean | number;
31
+ column_default: string | null;
32
+ };
33
+
34
+ type MssqlPrimaryKeyRow = {
35
+ table_schema: string;
36
+ table_name: string;
37
+ column_name: string;
38
+ key_ordinal: number;
39
+ };
40
+
41
+ type MssqlIndexRow = {
42
+ table_schema: string;
43
+ table_name: string;
44
+ index_name: string;
45
+ is_unique: boolean | number;
46
+ has_filter: boolean | number;
47
+ filter_definition: string | null;
48
+ };
49
+
50
+ type MssqlIndexColumnRow = {
51
+ table_schema: string;
52
+ table_name: string;
53
+ index_name: string;
54
+ column_name: string;
55
+ key_ordinal: number;
56
+ };
57
+
58
+ type MssqlForeignKeyRow = {
59
+ table_schema: string;
60
+ table_name: string;
61
+ column_name: string;
62
+ constraint_name: string;
63
+ referenced_schema: string;
64
+ referenced_table: string;
65
+ referenced_column: string;
66
+ delete_rule: string | null;
67
+ update_rule: string | null;
68
+ };
69
+
70
+ type ForeignKeyEntry = {
71
+ table: string;
72
+ column: string;
73
+ onDelete?: ReferentialAction;
74
+ onUpdate?: ReferentialAction;
75
+ name?: string;
76
+ };
77
+
78
+ const normalizeReferentialAction = (value: string | null | undefined): ReferentialAction | undefined => {
79
+ if (!value) return undefined;
80
+ const normalized = value.replace(/_/g, ' ').toUpperCase();
81
+ const allowed: ReferentialAction[] = ['NO ACTION', 'RESTRICT', 'CASCADE', 'SET NULL', 'SET DEFAULT'];
82
+ return allowed.includes(normalized as ReferentialAction) ? (normalized as ReferentialAction) : undefined;
83
+ };
84
+
85
+ const tableNode = (table: TableDef, alias: string): TableNode => ({
86
+ type: 'Table',
87
+ name: table.name,
88
+ schema: table.schema,
89
+ alias
90
+ });
91
+
92
+ const columnNode = (table: string, name: string, alias?: string): ColumnNode => ({
93
+ type: 'Column',
94
+ table,
95
+ name,
96
+ alias
97
+ });
98
+
99
+ const combineConditions = (...expressions: (ExpressionNode | undefined)[]): ExpressionNode | undefined => {
100
+ const filtered = expressions.filter(Boolean) as ExpressionNode[];
101
+ if (!filtered.length) return undefined;
102
+ if (filtered.length === 1) return filtered[0];
103
+ return and(...filtered);
104
+ };
105
+
106
+ export const mssqlIntrospector: SchemaIntrospector = {
107
+ async introspect(ctx: IntrospectContext, options: IntrospectOptions): Promise<DatabaseSchema> {
108
+ const schema = options.schema;
109
+ const schemaCondition = schema ? eq(columnNode('sch', 'name'), schema) : undefined;
110
+
111
+ const dataTypeExpression = buildMssqlDataType(
112
+ { table: 'ty', name: 'name' },
113
+ { table: 'c', name: 'max_length' },
114
+ { table: 'c', name: 'precision' },
115
+ { table: 'c', name: 'scale' }
116
+ ) as FunctionNode;
117
+
118
+ const defaultExpression = objectDefinition({ table: 'c', name: 'default_object_id' }) as FunctionNode;
119
+
120
+ const columnsQuery: SelectQueryNode = {
121
+ type: 'SelectQuery',
122
+ from: tableNode(SysColumns, 'c'),
123
+ columns: [
124
+ columnNode('sch', 'name', 'table_schema'),
125
+ columnNode('t', 'name', 'table_name'),
126
+ columnNode('c', 'name', 'column_name'),
127
+ { ...dataTypeExpression, alias: 'data_type' },
128
+ columnNode('c', 'is_nullable'),
129
+ columnNode('c', 'is_identity'),
130
+ { ...defaultExpression, alias: 'column_default' }
131
+ ],
132
+ joins: [
133
+ {
134
+ type: 'Join',
135
+ kind: 'INNER',
136
+ table: tableNode(SysTables, 't'),
137
+ condition: eq({ table: 't', name: 'object_id' }, { table: 'c', name: 'object_id' })
138
+ } as JoinNode,
139
+ {
140
+ type: 'Join',
141
+ kind: 'INNER',
142
+ table: tableNode(SysSchemas, 'sch'),
143
+ condition: eq({ table: 'sch', name: 'schema_id' }, { table: 't', name: 'schema_id' })
144
+ } as JoinNode,
145
+ {
146
+ type: 'Join',
147
+ kind: 'INNER',
148
+ table: tableNode(SysTypes, 'ty'),
149
+ condition: eq({ table: 'ty', name: 'user_type_id' }, { table: 'c', name: 'user_type_id' })
150
+ } as JoinNode
151
+ ],
152
+ where: combineConditions(
153
+ eq({ table: 't', name: 'is_ms_shipped' }, 0),
154
+ schemaCondition
155
+ )
156
+ };
157
+
158
+ const pkQuery: SelectQueryNode = {
159
+ type: 'SelectQuery',
160
+ from: tableNode(SysIndexes, 'i'),
161
+ columns: [
162
+ columnNode('sch', 'name', 'table_schema'),
163
+ columnNode('t', 'name', 'table_name'),
164
+ columnNode('c', 'name', 'column_name'),
165
+ columnNode('ic', 'key_ordinal', 'key_ordinal')
166
+ ],
167
+ joins: [
168
+ {
169
+ type: 'Join',
170
+ kind: 'INNER',
171
+ table: tableNode(SysIndexColumns, 'ic'),
172
+ condition: and(
173
+ eq({ table: 'ic', name: 'object_id' }, { table: 'i', name: 'object_id' }),
174
+ eq({ table: 'ic', name: 'index_id' }, { table: 'i', name: 'index_id' })
175
+ )
176
+ } as JoinNode,
177
+ {
178
+ type: 'Join',
179
+ kind: 'INNER',
180
+ table: tableNode(SysColumns, 'c'),
181
+ condition: and(
182
+ eq({ table: 'c', name: 'object_id' }, { table: 'ic', name: 'object_id' }),
183
+ eq({ table: 'c', name: 'column_id' }, { table: 'ic', name: 'column_id' })
184
+ )
185
+ } as JoinNode,
186
+ {
187
+ type: 'Join',
188
+ kind: 'INNER',
189
+ table: tableNode(SysTables, 't'),
190
+ condition: eq({ table: 't', name: 'object_id' }, { table: 'i', name: 'object_id' })
191
+ } as JoinNode,
192
+ {
193
+ type: 'Join',
194
+ kind: 'INNER',
195
+ table: tableNode(SysSchemas, 'sch'),
196
+ condition: eq({ table: 'sch', name: 'schema_id' }, { table: 't', name: 'schema_id' })
197
+ } as JoinNode
198
+ ],
199
+ where: combineConditions(
200
+ eq({ table: 'i', name: 'is_primary_key' }, 1),
201
+ schemaCondition
202
+ ),
203
+ orderBy: [
204
+ {
205
+ type: 'OrderBy',
206
+ term: columnNode('ic', 'key_ordinal'),
207
+ direction: 'ASC'
208
+ }
209
+ ]
210
+ };
211
+
212
+ const fkQuery: SelectQueryNode = {
213
+ type: 'SelectQuery',
214
+ from: tableNode(SysForeignKeyColumns, 'fkc'),
215
+ columns: [
216
+ columnNode('sch', 'name', 'table_schema'),
217
+ columnNode('t', 'name', 'table_name'),
218
+ columnNode('c', 'name', 'column_name'),
219
+ columnNode('fk', 'name', 'constraint_name'),
220
+ columnNode('rsch', 'name', 'referenced_schema'),
221
+ columnNode('rt', 'name', 'referenced_table'),
222
+ columnNode('rc', 'name', 'referenced_column'),
223
+ columnNode('fk', 'delete_referential_action_desc', 'delete_rule'),
224
+ columnNode('fk', 'update_referential_action_desc', 'update_rule')
225
+ ],
226
+ joins: [
227
+ {
228
+ type: 'Join',
229
+ kind: 'INNER',
230
+ table: tableNode(SysForeignKeys, 'fk'),
231
+ condition: eq({ table: 'fk', name: 'object_id' }, { table: 'fkc', name: 'constraint_object_id' })
232
+ } as JoinNode,
233
+ {
234
+ type: 'Join',
235
+ kind: 'INNER',
236
+ table: tableNode(SysTables, 't'),
237
+ condition: eq({ table: 't', name: 'object_id' }, { table: 'fkc', name: 'parent_object_id' })
238
+ } as JoinNode,
239
+ {
240
+ type: 'Join',
241
+ kind: 'INNER',
242
+ table: tableNode(SysSchemas, 'sch'),
243
+ condition: eq({ table: 'sch', name: 'schema_id' }, { table: 't', name: 'schema_id' })
244
+ } as JoinNode,
245
+ {
246
+ type: 'Join',
247
+ kind: 'INNER',
248
+ table: tableNode(SysColumns, 'c'),
249
+ condition: and(
250
+ eq({ table: 'c', name: 'object_id' }, { table: 'fkc', name: 'parent_object_id' }),
251
+ eq({ table: 'c', name: 'column_id' }, { table: 'fkc', name: 'parent_column_id' })
252
+ )
253
+ } as JoinNode,
254
+ {
255
+ type: 'Join',
256
+ kind: 'INNER',
257
+ table: tableNode(SysTables, 'rt'),
258
+ condition: eq({ table: 'rt', name: 'object_id' }, { table: 'fkc', name: 'referenced_object_id' })
259
+ } as JoinNode,
260
+ {
261
+ type: 'Join',
262
+ kind: 'INNER',
263
+ table: tableNode(SysSchemas, 'rsch'),
264
+ condition: eq({ table: 'rsch', name: 'schema_id' }, { table: 'rt', name: 'schema_id' })
265
+ } as JoinNode,
266
+ {
267
+ type: 'Join',
268
+ kind: 'INNER',
269
+ table: tableNode(SysColumns, 'rc'),
270
+ condition: and(
271
+ eq({ table: 'rc', name: 'object_id' }, { table: 'fkc', name: 'referenced_object_id' }),
272
+ eq({ table: 'rc', name: 'column_id' }, { table: 'fkc', name: 'referenced_column_id' })
273
+ )
274
+ } as JoinNode
275
+ ],
276
+ where: combineConditions(
277
+ eq({ table: 't', name: 'is_ms_shipped' }, 0),
278
+ schemaCondition
279
+ ),
280
+ orderBy: [
281
+ {
282
+ type: 'OrderBy',
283
+ term: columnNode('fk', 'name'),
284
+ direction: 'ASC'
285
+ },
286
+ {
287
+ type: 'OrderBy',
288
+ term: columnNode('fkc', 'constraint_column_id'),
289
+ direction: 'ASC'
290
+ }
291
+ ]
292
+ };
293
+
294
+ const indexQuery: SelectQueryNode = {
295
+ type: 'SelectQuery',
296
+ from: tableNode(SysIndexes, 'i'),
297
+ columns: [
298
+ columnNode('sch', 'name', 'table_schema'),
299
+ columnNode('t', 'name', 'table_name'),
300
+ columnNode('i', 'name', 'index_name'),
301
+ columnNode('i', 'is_unique'),
302
+ columnNode('i', 'has_filter'),
303
+ columnNode('i', 'filter_definition')
304
+ ],
305
+ joins: [
306
+ {
307
+ type: 'Join',
308
+ kind: 'INNER',
309
+ table: tableNode(SysTables, 't'),
310
+ condition: eq({ table: 't', name: 'object_id' }, { table: 'i', name: 'object_id' })
311
+ } as JoinNode,
312
+ {
313
+ type: 'Join',
314
+ kind: 'INNER',
315
+ table: tableNode(SysSchemas, 'sch'),
316
+ condition: eq({ table: 'sch', name: 'schema_id' }, { table: 't', name: 'schema_id' })
317
+ } as JoinNode
318
+ ],
319
+ where: combineConditions(
320
+ eq({ table: 'i', name: 'is_primary_key' }, 0),
321
+ eq({ table: 'i', name: 'is_hypothetical' }, 0),
322
+ schemaCondition
323
+ )
324
+ };
325
+
326
+ const indexColumnsQuery: SelectQueryNode = {
327
+ type: 'SelectQuery',
328
+ from: tableNode(SysIndexColumns, 'ic'),
329
+ columns: [
330
+ columnNode('sch', 'name', 'table_schema'),
331
+ columnNode('t', 'name', 'table_name'),
332
+ columnNode('i', 'name', 'index_name'),
333
+ columnNode('c', 'name', 'column_name'),
334
+ columnNode('ic', 'key_ordinal', 'key_ordinal')
335
+ ],
336
+ joins: [
337
+ {
338
+ type: 'Join',
339
+ kind: 'INNER',
340
+ table: tableNode(SysIndexes, 'i'),
341
+ condition: and(
342
+ eq({ table: 'ic', name: 'object_id' }, { table: 'i', name: 'object_id' }),
343
+ eq({ table: 'ic', name: 'index_id' }, { table: 'i', name: 'index_id' })
344
+ )
345
+ } as JoinNode,
346
+ {
347
+ type: 'Join',
348
+ kind: 'INNER',
349
+ table: tableNode(SysColumns, 'c'),
350
+ condition: and(
351
+ eq({ table: 'c', name: 'object_id' }, { table: 'ic', name: 'object_id' }),
352
+ eq({ table: 'c', name: 'column_id' }, { table: 'ic', name: 'column_id' })
353
+ )
354
+ } as JoinNode,
355
+ {
356
+ type: 'Join',
357
+ kind: 'INNER',
358
+ table: tableNode(SysTables, 't'),
359
+ condition: eq({ table: 't', name: 'object_id' }, { table: 'i', name: 'object_id' })
360
+ } as JoinNode,
361
+ {
362
+ type: 'Join',
363
+ kind: 'INNER',
364
+ table: tableNode(SysSchemas, 'sch'),
365
+ condition: eq({ table: 'sch', name: 'schema_id' }, { table: 't', name: 'schema_id' })
366
+ } as JoinNode
367
+ ],
368
+ where: combineConditions(
369
+ eq({ table: 'i', name: 'is_primary_key' }, 0),
370
+ schemaCondition
371
+ ),
372
+ orderBy: [
373
+ {
374
+ type: 'OrderBy',
375
+ term: columnNode('ic', 'key_ordinal'),
376
+ direction: 'ASC'
377
+ }
378
+ ]
379
+ };
380
+
381
+ const columnRows = (await runSelectNode<MssqlColumnRow>(columnsQuery, ctx)) as MssqlColumnRow[];
382
+ const pkRows = (await runSelectNode<MssqlPrimaryKeyRow>(pkQuery, ctx)) as MssqlPrimaryKeyRow[];
383
+ const fkRows = (await runSelectNode<MssqlForeignKeyRow>(fkQuery, ctx)) as MssqlForeignKeyRow[];
384
+ const indexRows = (await runSelectNode<MssqlIndexRow>(indexQuery, ctx)) as MssqlIndexRow[];
385
+ const indexColsRows = (await runSelectNode<MssqlIndexColumnRow>(indexColumnsQuery, ctx)) as MssqlIndexColumnRow[];
386
+
387
+ const pkMap = new Map<string, string[]>();
388
+ pkRows.forEach(r => {
389
+ const key = `${r.table_schema}.${r.table_name}`;
390
+ const list = pkMap.get(key) || [];
391
+ list.push(r.column_name);
392
+ pkMap.set(key, list);
393
+ });
394
+
395
+ const fkMap = new Map<string, ForeignKeyEntry[]>();
396
+ fkRows.forEach(r => {
397
+ const key = `${r.table_schema}.${r.table_name}.${r.column_name}`;
398
+ const list = fkMap.get(key) || [];
399
+ list.push({
400
+ table: `${r.referenced_schema}.${r.referenced_table}`,
401
+ column: r.referenced_column,
402
+ onDelete: normalizeReferentialAction(r.delete_rule),
403
+ onUpdate: normalizeReferentialAction(r.update_rule),
404
+ name: r.constraint_name
405
+ });
406
+ fkMap.set(key, list);
407
+ });
408
+
409
+ const indexColumnsMap = new Map<string, { column: string; order: number }[]>();
410
+ indexColsRows.forEach(r => {
411
+ const key = `${r.table_schema}.${r.table_name}.${r.index_name}`;
412
+ const list = indexColumnsMap.get(key) || [];
413
+ list.push({ column: r.column_name, order: r.key_ordinal });
414
+ indexColumnsMap.set(key, list);
415
+ });
416
+
417
+ const tablesByKey = new Map<string, DatabaseTable>();
418
+
419
+ columnRows.forEach(r => {
420
+ if (!shouldIncludeTable(r.table_name, options)) return;
421
+ const key = `${r.table_schema}.${r.table_name}`;
422
+ if (!tablesByKey.has(key)) {
423
+ tablesByKey.set(key, {
424
+ name: r.table_name,
425
+ schema: r.table_schema,
426
+ columns: [],
427
+ primaryKey: pkMap.get(key) || [],
428
+ indexes: []
429
+ });
430
+ }
431
+ const table = tablesByKey.get(key)!;
432
+ const column: DatabaseColumn = {
433
+ name: r.column_name,
434
+ type: r.data_type,
435
+ notNull: r.is_nullable === false || r.is_nullable === 0,
436
+ default: r.column_default ?? undefined,
437
+ autoIncrement: !!r.is_identity
438
+ };
439
+ const fk = fkMap.get(`${key}.${r.column_name}`)?.[0];
440
+ if (fk) {
441
+ column.references = {
442
+ table: fk.table,
443
+ column: fk.column,
444
+ onDelete: fk.onDelete,
445
+ onUpdate: fk.onUpdate,
446
+ name: fk.name
447
+ };
448
+ }
449
+ table.columns.push(column);
450
+ });
451
+
452
+ indexRows.forEach(r => {
453
+ const key = `${r.table_schema}.${r.table_name}`;
454
+ const table = tablesByKey.get(key);
455
+ if (!table) return;
456
+ const cols = (indexColumnsMap.get(`${r.table_schema}.${r.table_name}.${r.index_name}`) || [])
457
+ .sort((a, b) => a.order - b.order)
458
+ .map(c => ({ column: c.column }));
459
+ const idx: DatabaseIndex = {
460
+ name: r.index_name,
461
+ columns: cols,
462
+ unique: !!r.is_unique,
463
+ where: r.has_filter ? r.filter_definition ?? undefined : undefined
464
+ };
465
+ table.indexes = table.indexes || [];
466
+ table.indexes.push(idx);
467
+ });
468
+
469
+ return { tables: Array.from(tablesByKey.values()) };
470
+ }
471
+ };