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,125 +1,336 @@
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 MySQL column information. */
7
- type MysqlColumnRow = {
8
- table_schema: string;
9
- table_name: string;
10
- column_name: string;
11
- data_type: string;
12
- is_nullable: string;
13
- column_default: string | null;
14
- extra: string | null;
15
- };
16
-
17
- type MysqlPrimaryKeyRow = {
18
- table_schema: string;
19
- table_name: string;
20
- column_name: string;
21
- };
22
-
23
- type MysqlIndexRow = {
24
- table_schema: string;
25
- table_name: string;
26
- index_name: string;
27
- non_unique: number;
28
- cols: string | null;
29
- };
30
-
31
- /** MySQL schema introspector. */
32
- export const mysqlIntrospector: SchemaIntrospector = {
33
- async introspect(ctx: { executor: DbExecutor }, options: IntrospectOptions): Promise<DatabaseSchema> {
34
- const schema = options.schema;
35
- const filterClause = schema ? 'table_schema = ?' : 'table_schema = database()';
36
- const params = schema ? [schema] : [];
37
-
38
- const columnRows = (await queryRows(
39
- ctx.executor,
40
- `
41
- SELECT table_schema, table_name, column_name, data_type, is_nullable, column_default, extra
42
- FROM information_schema.columns
43
- WHERE ${filterClause}
44
- ORDER BY table_name, ordinal_position
45
- `,
46
- params
47
- )) as MysqlColumnRow[];
48
-
49
- const pkRows = (await queryRows(
50
- ctx.executor,
51
- `
52
- SELECT table_schema, table_name, column_name
53
- FROM information_schema.key_column_usage
54
- WHERE constraint_name = 'PRIMARY' AND ${filterClause}
55
- ORDER BY ordinal_position
56
- `,
57
- params
58
- )) as MysqlPrimaryKeyRow[];
59
-
60
- const pkMap = new Map<string, string[]>();
61
- pkRows.forEach(r => {
62
- const key = `${r.table_schema}.${r.table_name}`;
63
- const list = pkMap.get(key) || [];
64
- list.push(r.column_name);
65
- pkMap.set(key, list);
66
- });
67
-
68
- const indexRows = (await queryRows(
69
- ctx.executor,
70
- `
71
- SELECT
72
- table_schema,
73
- table_name,
74
- index_name,
75
- non_unique,
76
- GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols
77
- FROM information_schema.statistics
78
- WHERE ${filterClause} AND index_name <> 'PRIMARY'
79
- GROUP BY table_schema, table_name, index_name, non_unique
80
- `,
81
- params
82
- )) as MysqlIndexRow[];
83
-
84
- const tablesByKey = new Map<string, DatabaseTable>();
85
-
86
- columnRows.forEach(r => {
87
- const key = `${r.table_schema}.${r.table_name}`;
88
- if (!shouldIncludeTable(r.table_name, options)) return;
89
- if (!tablesByKey.has(key)) {
90
- tablesByKey.set(key, {
91
- name: r.table_name,
92
- schema: r.table_schema,
93
- columns: [],
94
- primaryKey: pkMap.get(key) || [],
95
- indexes: []
96
- });
97
- }
98
- const cols = tablesByKey.get(key)!;
99
- const column: DatabaseColumn = {
100
- name: r.column_name,
101
- type: r.data_type,
102
- notNull: r.is_nullable === 'NO',
103
- default: r.column_default ?? undefined,
104
- autoIncrement: typeof r.extra === 'string' && r.extra.includes('auto_increment')
105
- };
106
- cols.columns.push(column);
107
- });
108
-
109
- indexRows.forEach(r => {
110
- const key = `${r.table_schema}.${r.table_name}`;
111
- const table = tablesByKey.get(key);
112
- if (!table) return;
113
- const cols = (typeof r.cols === 'string' ? r.cols.split(',') : []).map((c: string) => ({ column: c.trim() }));
114
- const idx: DatabaseIndex = {
115
- name: r.index_name,
116
- columns: cols,
117
- unique: r.non_unique === 0
118
- };
119
- table.indexes = table.indexes || [];
120
- table.indexes.push(idx);
121
- });
122
-
123
- return { tables: Array.from(tablesByKey.values()) };
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: undefined,
165
- onUpdate: undefined
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 { toRows } from './utils.js';
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
- const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
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
- const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
38
- const [first] = results;
39
- return toRows(first) as T[];
34
+ return (await queryRows(ctx.executor, compiled.sql, compiled.params)) as T[];
40
35
  }