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.
- package/dist/index.cjs +1092 -323
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -9
- package/dist/index.d.ts +99 -9
- package/dist/index.js +1087 -323
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/generate-entities.mjs +36 -9
- package/src/codegen/typescript.ts +22 -10
- package/src/core/ast/adapters.ts +2 -1
- package/src/core/ast/expression-builders.ts +13 -0
- package/src/core/ast/expression-nodes.ts +25 -5
- package/src/core/ast/expression-visitor.ts +5 -0
- package/src/core/ast/query.ts +9 -1
- package/src/core/ddl/dialects/base-schema-dialect.ts +2 -1
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +10 -23
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +10 -24
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +10 -23
- package/src/core/ddl/dialects/render-reference.test.ts +2 -1
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -23
- package/src/core/ddl/introspect/catalogs/index.ts +4 -1
- package/src/core/ddl/introspect/catalogs/mssql.ts +126 -0
- package/src/core/ddl/introspect/catalogs/mysql.ts +89 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +2 -1
- package/src/core/ddl/introspect/catalogs/sqlite.ts +47 -0
- package/src/core/ddl/introspect/functions/mssql.ts +84 -0
- package/src/core/ddl/introspect/mssql.ts +471 -194
- package/src/core/ddl/introspect/mysql.ts +336 -125
- package/src/core/ddl/introspect/postgres.ts +45 -5
- package/src/core/ddl/introspect/run-select.ts +3 -8
- package/src/core/ddl/introspect/sqlite.ts +113 -59
- package/src/core/ddl/schema-dialect.ts +2 -1
- package/src/core/ddl/schema-diff.ts +2 -1
- package/src/core/ddl/schema-generator.ts +2 -1
- package/src/core/ddl/schema-types.ts +3 -1
- package/src/core/ddl/sql-writing.ts +2 -1
- package/src/core/dialect/abstract.ts +12 -1
- package/src/core/dialect/mssql/index.ts +4 -10
- package/src/core/functions/datetime.ts +2 -1
- package/src/core/functions/numeric.ts +2 -1
- package/src/core/functions/text.ts +2 -1
- package/src/decorators/{column.ts → column-decorator.ts} +4 -1
- package/src/decorators/index.ts +1 -1
- package/src/index.ts +2 -1
- package/src/orm/entity-metadata.ts +2 -1
- package/src/orm/lazy-batch.ts +2 -1
- package/src/orm/orm-session.ts +2 -1
- package/src/query-builder/column-selector.ts +2 -1
- package/src/query-builder/delete.ts +2 -1
- package/src/query-builder/insert.ts +2 -1
- package/src/query-builder/query-ast-service.ts +4 -3
- package/src/query-builder/relation-projection-helper.ts +2 -1
- package/src/query-builder/relation-service.ts +2 -1
- package/src/query-builder/select/predicate-facet.ts +2 -1
- package/src/query-builder/select/projection-facet.ts +2 -1
- package/src/query-builder/select-helpers.ts +2 -1
- package/src/query-builder/select-query-state.ts +2 -0
- package/src/query-builder/select.ts +2 -1
- package/src/query-builder/update.ts +2 -1
- package/src/schema/{column.ts → column-types.ts} +317 -290
- package/src/schema/table-guards.ts +1 -1
- package/src/schema/table.ts +1 -1
- package/src/schema/types.ts +10 -8
|
@@ -1,125 +1,336 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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, neq, and, isNotNull } from '../../ast/expression-builders.js';
|
|
11
|
+
import { groupConcat } from '../../ast/aggregate-functions.js';
|
|
12
|
+
import type { TableDef } from '../../../schema/table.js';
|
|
13
|
+
import {
|
|
14
|
+
InformationSchemaTables,
|
|
15
|
+
InformationSchemaColumns,
|
|
16
|
+
InformationSchemaKeyColumnUsage,
|
|
17
|
+
InformationSchemaReferentialConstraints,
|
|
18
|
+
InformationSchemaStatistics
|
|
19
|
+
} from './catalogs/mysql.js';
|
|
20
|
+
|
|
21
|
+
type MysqlColumnRow = {
|
|
22
|
+
table_schema: string;
|
|
23
|
+
table_name: string;
|
|
24
|
+
column_name: string;
|
|
25
|
+
column_type: string;
|
|
26
|
+
data_type: string;
|
|
27
|
+
is_nullable: string;
|
|
28
|
+
column_default: string | null;
|
|
29
|
+
extra: string | null;
|
|
30
|
+
column_comment: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type MysqlPrimaryKeyRow = {
|
|
34
|
+
table_schema: string;
|
|
35
|
+
table_name: string;
|
|
36
|
+
column_name: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type MysqlTableRow = {
|
|
40
|
+
table_schema: string;
|
|
41
|
+
table_name: string;
|
|
42
|
+
table_comment: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type MysqlIndexRow = {
|
|
46
|
+
table_schema: string;
|
|
47
|
+
table_name: string;
|
|
48
|
+
index_name: string;
|
|
49
|
+
non_unique: number;
|
|
50
|
+
cols: string | null;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type MysqlForeignKeyRow = {
|
|
54
|
+
table_schema: string;
|
|
55
|
+
table_name: string;
|
|
56
|
+
column_name: string;
|
|
57
|
+
constraint_name: string;
|
|
58
|
+
referenced_table_schema: string;
|
|
59
|
+
referenced_table_name: string;
|
|
60
|
+
referenced_column_name: string;
|
|
61
|
+
delete_rule: string;
|
|
62
|
+
update_rule: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
type MysqlForeignKeyEntry = {
|
|
66
|
+
table: string;
|
|
67
|
+
column: string;
|
|
68
|
+
onDelete?: string;
|
|
69
|
+
onUpdate?: string;
|
|
70
|
+
name?: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const tableNode = (table: TableDef, alias: string): TableNode => ({
|
|
74
|
+
type: 'Table',
|
|
75
|
+
name: table.name,
|
|
76
|
+
schema: table.schema,
|
|
77
|
+
alias
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const columnNode = (table: string, name: string, alias?: string): ColumnNode => ({
|
|
81
|
+
type: 'Column',
|
|
82
|
+
table,
|
|
83
|
+
name,
|
|
84
|
+
alias
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const combineConditions = (...expressions: (ExpressionNode | undefined)[]): ExpressionNode | undefined => {
|
|
88
|
+
const filtered = expressions.filter(Boolean) as ExpressionNode[];
|
|
89
|
+
if (!filtered.length) return undefined;
|
|
90
|
+
if (filtered.length === 1) return filtered[0];
|
|
91
|
+
return and(...filtered);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const databaseFunction: FunctionNode = {
|
|
95
|
+
type: 'Function',
|
|
96
|
+
name: 'DATABASE',
|
|
97
|
+
fn: 'DATABASE',
|
|
98
|
+
args: []
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const mysqlIntrospector: SchemaIntrospector = {
|
|
102
|
+
async introspect(ctx: IntrospectContext, options: IntrospectOptions): Promise<DatabaseSchema> {
|
|
103
|
+
const schema = options.schema;
|
|
104
|
+
|
|
105
|
+
const buildSchemaCondition = (alias: string): ExpressionNode =>
|
|
106
|
+
schema
|
|
107
|
+
? eq(columnNode(alias, 'table_schema'), schema)
|
|
108
|
+
: eq(columnNode(alias, 'table_schema'), databaseFunction);
|
|
109
|
+
|
|
110
|
+
const tablesQuery: SelectQueryNode = {
|
|
111
|
+
type: 'SelectQuery',
|
|
112
|
+
from: tableNode(InformationSchemaTables, 't'),
|
|
113
|
+
columns: [
|
|
114
|
+
columnNode('t', 'table_schema'),
|
|
115
|
+
columnNode('t', 'table_name'),
|
|
116
|
+
columnNode('t', 'table_comment')
|
|
117
|
+
],
|
|
118
|
+
joins: [],
|
|
119
|
+
where: buildSchemaCondition('t')
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const columnsQuery: SelectQueryNode = {
|
|
123
|
+
type: 'SelectQuery',
|
|
124
|
+
from: tableNode(InformationSchemaColumns, 'c'),
|
|
125
|
+
columns: [
|
|
126
|
+
columnNode('c', 'table_schema'),
|
|
127
|
+
columnNode('c', 'table_name'),
|
|
128
|
+
columnNode('c', 'column_name'),
|
|
129
|
+
columnNode('c', 'column_type'),
|
|
130
|
+
columnNode('c', 'data_type'),
|
|
131
|
+
columnNode('c', 'is_nullable'),
|
|
132
|
+
columnNode('c', 'column_default'),
|
|
133
|
+
columnNode('c', 'extra'),
|
|
134
|
+
columnNode('c', 'column_comment')
|
|
135
|
+
],
|
|
136
|
+
joins: [],
|
|
137
|
+
where: buildSchemaCondition('c'),
|
|
138
|
+
orderBy: [
|
|
139
|
+
{
|
|
140
|
+
type: 'OrderBy',
|
|
141
|
+
term: columnNode('c', 'table_name'),
|
|
142
|
+
direction: 'ASC'
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: 'OrderBy',
|
|
146
|
+
term: columnNode('c', 'ordinal_position'),
|
|
147
|
+
direction: 'ASC'
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const pkQuery: SelectQueryNode = {
|
|
153
|
+
type: 'SelectQuery',
|
|
154
|
+
from: tableNode(InformationSchemaKeyColumnUsage, 'kcu'),
|
|
155
|
+
columns: [
|
|
156
|
+
columnNode('kcu', 'table_schema'),
|
|
157
|
+
columnNode('kcu', 'table_name'),
|
|
158
|
+
columnNode('kcu', 'column_name')
|
|
159
|
+
],
|
|
160
|
+
joins: [],
|
|
161
|
+
where: combineConditions(
|
|
162
|
+
eq(columnNode('kcu', 'constraint_name'), 'PRIMARY'),
|
|
163
|
+
buildSchemaCondition('kcu')
|
|
164
|
+
),
|
|
165
|
+
orderBy: [
|
|
166
|
+
{
|
|
167
|
+
type: 'OrderBy',
|
|
168
|
+
term: columnNode('kcu', 'ordinal_position'),
|
|
169
|
+
direction: 'ASC'
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const fkQuery: SelectQueryNode = {
|
|
175
|
+
type: 'SelectQuery',
|
|
176
|
+
from: tableNode(InformationSchemaKeyColumnUsage, 'kcu'),
|
|
177
|
+
columns: [
|
|
178
|
+
columnNode('kcu', 'table_schema'),
|
|
179
|
+
columnNode('kcu', 'table_name'),
|
|
180
|
+
columnNode('kcu', 'column_name'),
|
|
181
|
+
columnNode('kcu', 'constraint_name'),
|
|
182
|
+
columnNode('kcu', 'referenced_table_schema'),
|
|
183
|
+
columnNode('kcu', 'referenced_table_name'),
|
|
184
|
+
columnNode('kcu', 'referenced_column_name'),
|
|
185
|
+
columnNode('rc', 'delete_rule'),
|
|
186
|
+
columnNode('rc', 'update_rule')
|
|
187
|
+
],
|
|
188
|
+
joins: [
|
|
189
|
+
{
|
|
190
|
+
type: 'Join',
|
|
191
|
+
kind: 'INNER',
|
|
192
|
+
table: tableNode(InformationSchemaReferentialConstraints, 'rc'),
|
|
193
|
+
condition: and(
|
|
194
|
+
eq({ table: 'rc', name: 'constraint_schema' }, { table: 'kcu', name: 'constraint_schema' }),
|
|
195
|
+
eq({ table: 'rc', name: 'constraint_name' }, { table: 'kcu', name: 'constraint_name' })
|
|
196
|
+
)
|
|
197
|
+
} as JoinNode
|
|
198
|
+
],
|
|
199
|
+
where: combineConditions(
|
|
200
|
+
isNotNull(columnNode('kcu', 'referenced_table_name')),
|
|
201
|
+
buildSchemaCondition('kcu')
|
|
202
|
+
),
|
|
203
|
+
orderBy: [
|
|
204
|
+
{
|
|
205
|
+
type: 'OrderBy',
|
|
206
|
+
term: columnNode('kcu', 'table_name'),
|
|
207
|
+
direction: 'ASC'
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
type: 'OrderBy',
|
|
211
|
+
term: columnNode('kcu', 'ordinal_position'),
|
|
212
|
+
direction: 'ASC'
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const indexQuery: SelectQueryNode = {
|
|
218
|
+
type: 'SelectQuery',
|
|
219
|
+
from: tableNode(InformationSchemaStatistics, 'stats'),
|
|
220
|
+
columns: [
|
|
221
|
+
columnNode('stats', 'table_schema'),
|
|
222
|
+
columnNode('stats', 'table_name'),
|
|
223
|
+
columnNode('stats', 'index_name'),
|
|
224
|
+
columnNode('stats', 'non_unique'),
|
|
225
|
+
{
|
|
226
|
+
...groupConcat(columnNode('stats', 'column_name'), {
|
|
227
|
+
orderBy: [{ column: columnNode('stats', 'seq_in_index') }]
|
|
228
|
+
}),
|
|
229
|
+
alias: 'cols'
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
joins: [],
|
|
233
|
+
where: combineConditions(
|
|
234
|
+
neq(columnNode('stats', 'index_name'), 'PRIMARY'),
|
|
235
|
+
buildSchemaCondition('stats')
|
|
236
|
+
),
|
|
237
|
+
groupBy: [
|
|
238
|
+
columnNode('stats', 'table_schema'),
|
|
239
|
+
columnNode('stats', 'table_name'),
|
|
240
|
+
columnNode('stats', 'index_name'),
|
|
241
|
+
columnNode('stats', 'non_unique')
|
|
242
|
+
]
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const tableRows = (await runSelectNode<MysqlTableRow>(tablesQuery, ctx)) as MysqlTableRow[];
|
|
246
|
+
const columnRows = (await runSelectNode<MysqlColumnRow>(columnsQuery, ctx)) as MysqlColumnRow[];
|
|
247
|
+
const pkRows = (await runSelectNode<MysqlPrimaryKeyRow>(pkQuery, ctx)) as MysqlPrimaryKeyRow[];
|
|
248
|
+
const fkRows = (await runSelectNode<MysqlForeignKeyRow>(fkQuery, ctx)) as MysqlForeignKeyRow[];
|
|
249
|
+
const indexRows = (await runSelectNode<MysqlIndexRow>(indexQuery, ctx)) as MysqlIndexRow[];
|
|
250
|
+
|
|
251
|
+
const tableComments = new Map<string, string>();
|
|
252
|
+
tableRows.forEach(r => {
|
|
253
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
254
|
+
if (r.table_comment) {
|
|
255
|
+
tableComments.set(key, r.table_comment);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const pkMap = new Map<string, string[]>();
|
|
260
|
+
pkRows.forEach(r => {
|
|
261
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
262
|
+
const list = pkMap.get(key) || [];
|
|
263
|
+
list.push(r.column_name);
|
|
264
|
+
pkMap.set(key, list);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const fkMap = new Map<string, MysqlForeignKeyEntry[]>();
|
|
268
|
+
fkRows.forEach(r => {
|
|
269
|
+
const key = `${r.table_schema}.${r.table_name}.${r.column_name}`;
|
|
270
|
+
const list = fkMap.get(key) || [];
|
|
271
|
+
list.push({
|
|
272
|
+
table: `${r.referenced_table_schema}.${r.referenced_table_name}`,
|
|
273
|
+
column: r.referenced_column_name,
|
|
274
|
+
onDelete: r.delete_rule,
|
|
275
|
+
onUpdate: r.update_rule,
|
|
276
|
+
name: r.constraint_name
|
|
277
|
+
});
|
|
278
|
+
fkMap.set(key, list);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const tablesByKey = new Map<string, DatabaseTable>();
|
|
282
|
+
|
|
283
|
+
columnRows.forEach(r => {
|
|
284
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
285
|
+
if (!shouldIncludeTable(r.table_name, options)) return;
|
|
286
|
+
if (!tablesByKey.has(key)) {
|
|
287
|
+
tablesByKey.set(key, {
|
|
288
|
+
name: r.table_name,
|
|
289
|
+
schema: r.table_schema,
|
|
290
|
+
columns: [],
|
|
291
|
+
primaryKey: pkMap.get(key) || [],
|
|
292
|
+
indexes: [],
|
|
293
|
+
comment: tableComments.get(key) || undefined
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
const table = tablesByKey.get(key)!;
|
|
297
|
+
const columnType = r.column_type || r.data_type;
|
|
298
|
+
const comment = r.column_comment?.trim() ? r.column_comment : undefined;
|
|
299
|
+
const column: DatabaseColumn = {
|
|
300
|
+
name: r.column_name,
|
|
301
|
+
type: columnType,
|
|
302
|
+
notNull: r.is_nullable === 'NO',
|
|
303
|
+
default: r.column_default ?? undefined,
|
|
304
|
+
autoIncrement: typeof r.extra === 'string' && r.extra.includes('auto_increment'),
|
|
305
|
+
comment
|
|
306
|
+
};
|
|
307
|
+
const fk = fkMap.get(`${key}.${r.column_name}`)?.[0];
|
|
308
|
+
if (fk) {
|
|
309
|
+
column.references = {
|
|
310
|
+
table: fk.table,
|
|
311
|
+
column: fk.column,
|
|
312
|
+
onDelete: fk.onDelete as ReferentialAction | undefined,
|
|
313
|
+
onUpdate: fk.onUpdate as ReferentialAction | undefined,
|
|
314
|
+
name: fk.name
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
table.columns.push(column);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
indexRows.forEach(r => {
|
|
321
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
322
|
+
const table = tablesByKey.get(key);
|
|
323
|
+
if (!table) return;
|
|
324
|
+
const cols = (typeof r.cols === 'string' ? r.cols.split(',') : []).map(c => ({ column: c.trim() }));
|
|
325
|
+
const idx: DatabaseIndex = {
|
|
326
|
+
name: r.index_name,
|
|
327
|
+
columns: cols,
|
|
328
|
+
unique: r.non_unique === 0
|
|
329
|
+
};
|
|
330
|
+
table.indexes = table.indexes || [];
|
|
331
|
+
table.indexes.push(idx);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
return { tables: Array.from(tablesByKey.values()) };
|
|
335
|
+
}
|
|
336
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SchemaIntrospector, IntrospectOptions } from './types.js';
|
|
2
|
-
import { shouldIncludeTable } from './utils.js';
|
|
2
|
+
import { shouldIncludeTable, queryRows } from './utils.js';
|
|
3
3
|
import { DatabaseSchema, DatabaseTable, DatabaseIndex, DatabaseColumn } from '../schema-types.js';
|
|
4
|
-
import type { ReferentialAction } from '../../../schema/column.js';
|
|
4
|
+
import type { ReferentialAction } from '../../../schema/column-types.js';
|
|
5
5
|
import type { IntrospectContext } from './context.js';
|
|
6
6
|
import { PgInformationSchemaColumns } from './catalogs/postgres.js';
|
|
7
7
|
import { PgKeyColumnUsage, PgTableConstraints, PgConstraintColumnUsage, PgReferentialConstraints } from './catalogs/postgres.js';
|
|
@@ -42,6 +42,8 @@ type ForeignKeyIntrospectRow = {
|
|
|
42
42
|
foreign_table_schema: string;
|
|
43
43
|
foreign_table_name: string;
|
|
44
44
|
foreign_column_name: string;
|
|
45
|
+
delete_rule: ReferentialAction;
|
|
46
|
+
update_rule: ReferentialAction;
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
/** Represents a foreign key reference entry with optional referential actions. */
|
|
@@ -52,6 +54,13 @@ type ForeignKeyEntry = {
|
|
|
52
54
|
onUpdate?: ReferentialAction;
|
|
53
55
|
};
|
|
54
56
|
|
|
57
|
+
type ColumnCommentRow = {
|
|
58
|
+
table_schema: string;
|
|
59
|
+
table_name: string;
|
|
60
|
+
column_name: string;
|
|
61
|
+
description: string | null;
|
|
62
|
+
};
|
|
63
|
+
|
|
55
64
|
/** Row type for PostgreSQL index query results from pg_catalog tables. */
|
|
56
65
|
type IndexQueryRow = {
|
|
57
66
|
table_schema: string;
|
|
@@ -102,6 +111,32 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
102
111
|
.orderBy(PgInformationSchemaColumns.columns.ordinal_position);
|
|
103
112
|
|
|
104
113
|
const columnRows = await runSelect<ColumnIntrospectRow>(qbColumns, ctx);
|
|
114
|
+
const columnCommentRows = (await queryRows(
|
|
115
|
+
ctx.executor,
|
|
116
|
+
`
|
|
117
|
+
SELECT
|
|
118
|
+
ns.nspname AS table_schema,
|
|
119
|
+
cls.relname AS table_name,
|
|
120
|
+
att.attname AS column_name,
|
|
121
|
+
pg_catalog.col_description(cls.oid, att.attnum) AS description
|
|
122
|
+
FROM pg_catalog.pg_attribute att
|
|
123
|
+
JOIN pg_catalog.pg_class cls ON cls.oid = att.attrelid
|
|
124
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cls.relnamespace
|
|
125
|
+
WHERE ns.nspname = $1
|
|
126
|
+
AND att.attnum > 0
|
|
127
|
+
AND NOT att.attisdropped
|
|
128
|
+
`,
|
|
129
|
+
[schema]
|
|
130
|
+
)) as ColumnCommentRow[];
|
|
131
|
+
const columnComments = new Map<string, string>();
|
|
132
|
+
columnCommentRows.forEach(r => {
|
|
133
|
+
if (!shouldIncludeTable(r.table_name, options)) return;
|
|
134
|
+
if (!r.description) return;
|
|
135
|
+
const key = `${r.table_schema}.${r.table_name}.${r.column_name}`;
|
|
136
|
+
const trimmed = r.description.trim();
|
|
137
|
+
if (!trimmed) return;
|
|
138
|
+
columnComments.set(key, trimmed);
|
|
139
|
+
});
|
|
105
140
|
|
|
106
141
|
// Primary key columns query
|
|
107
142
|
const qbPk = new SelectQueryBuilder(PgKeyColumnUsage)
|
|
@@ -143,7 +178,9 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
143
178
|
constraint_name: PgKeyColumnUsage.columns.constraint_name,
|
|
144
179
|
foreign_table_schema: PgConstraintColumnUsage.columns.table_schema,
|
|
145
180
|
foreign_table_name: PgConstraintColumnUsage.columns.table_name,
|
|
146
|
-
foreign_column_name: PgConstraintColumnUsage.columns.column_name
|
|
181
|
+
foreign_column_name: PgConstraintColumnUsage.columns.column_name,
|
|
182
|
+
delete_rule: PgReferentialConstraints.columns.delete_rule,
|
|
183
|
+
update_rule: PgReferentialConstraints.columns.update_rule
|
|
147
184
|
})
|
|
148
185
|
.innerJoin(PgTableConstraints, eq(PgTableConstraints.columns.constraint_name, PgKeyColumnUsage.columns.constraint_name))
|
|
149
186
|
.innerJoin(PgConstraintColumnUsage, eq(PgConstraintColumnUsage.columns.constraint_name, PgTableConstraints.columns.constraint_name))
|
|
@@ -161,8 +198,8 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
161
198
|
existing.push({
|
|
162
199
|
table: `${r.foreign_table_schema}.${r.foreign_table_name}`,
|
|
163
200
|
column: r.foreign_column_name,
|
|
164
|
-
onDelete:
|
|
165
|
-
onUpdate:
|
|
201
|
+
onDelete: r.delete_rule,
|
|
202
|
+
onUpdate: r.update_rule
|
|
166
203
|
});
|
|
167
204
|
fkMap.set(key, existing);
|
|
168
205
|
}
|
|
@@ -275,12 +312,15 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
275
312
|
});
|
|
276
313
|
}
|
|
277
314
|
const cols = tablesByKey.get(key)!;
|
|
315
|
+
const commentKey = `${r.table_schema}.${r.table_name}.${r.column_name}`;
|
|
316
|
+
const columnComment = columnComments.get(commentKey);
|
|
278
317
|
const fk = fkMap.get(`${r.table_schema}.${r.table_name}.${r.column_name}`)?.[0];
|
|
279
318
|
const column: DatabaseColumn = {
|
|
280
319
|
name: r.column_name,
|
|
281
320
|
type: r.data_type,
|
|
282
321
|
notNull: r.is_nullable === 'NO',
|
|
283
322
|
default: r.column_default ?? undefined,
|
|
323
|
+
comment: columnComment ?? undefined,
|
|
284
324
|
references: fk
|
|
285
325
|
? {
|
|
286
326
|
table: fk.table,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { IntrospectContext } from './context.js';
|
|
2
2
|
import type { SelectQueryNode } from '../../ast/query.js';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { queryRows } from './utils.js';
|
|
5
5
|
|
|
6
6
|
/** A source that can provide a select query AST. */
|
|
7
7
|
type SelectQuerySource = { getAST(): SelectQueryNode };
|
|
@@ -18,10 +18,7 @@ export async function runSelect<T = Record<string, unknown>>(
|
|
|
18
18
|
): Promise<T[]> {
|
|
19
19
|
const ast = qb.getAST();
|
|
20
20
|
const compiled = ctx.dialect.compileSelect(ast);
|
|
21
|
-
|
|
22
|
-
// executor returns QueryResult[]; take the first result set and map to rows
|
|
23
|
-
const [first] = results;
|
|
24
|
-
return toRows(first) as T[];
|
|
21
|
+
return (await queryRows(ctx.executor, compiled.sql, compiled.params)) as T[];
|
|
25
22
|
}
|
|
26
23
|
|
|
27
24
|
export default runSelect;
|
|
@@ -34,7 +31,5 @@ export default runSelect;
|
|
|
34
31
|
*/
|
|
35
32
|
export async function runSelectNode<T = Record<string, unknown>>(ast: SelectQueryNode, ctx: IntrospectContext): Promise<T[]> {
|
|
36
33
|
const compiled = ctx.dialect.compileSelect(ast);
|
|
37
|
-
|
|
38
|
-
const [first] = results;
|
|
39
|
-
return toRows(first) as T[];
|
|
34
|
+
return (await queryRows(ctx.executor, compiled.sql, compiled.params)) as T[];
|
|
40
35
|
}
|