metal-orm 1.0.50 → 1.0.51

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.
@@ -54,12 +54,18 @@ type ForeignKeyEntry = {
54
54
  onUpdate?: ReferentialAction;
55
55
  };
56
56
 
57
- type ColumnCommentRow = {
58
- table_schema: string;
59
- table_name: string;
60
- column_name: string;
61
- description: string | null;
62
- };
57
+ type ColumnCommentRow = {
58
+ table_schema: string;
59
+ table_name: string;
60
+ column_name: string;
61
+ description: string | null;
62
+ };
63
+
64
+ type TableCommentRow = {
65
+ table_schema: string;
66
+ table_name: string;
67
+ description: string | null;
68
+ };
63
69
 
64
70
  /** Row type for PostgreSQL index query results from pg_catalog tables. */
65
71
  type IndexQueryRow = {
@@ -111,32 +117,55 @@ export const postgresIntrospector: SchemaIntrospector = {
111
117
  .orderBy(PgInformationSchemaColumns.columns.ordinal_position);
112
118
 
113
119
  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
- });
120
+ const columnCommentRows = (await queryRows(
121
+ ctx.executor,
122
+ `
123
+ SELECT
124
+ ns.nspname AS table_schema,
125
+ cls.relname AS table_name,
126
+ att.attname AS column_name,
127
+ pg_catalog.col_description(cls.oid, att.attnum) AS description
128
+ FROM pg_catalog.pg_attribute att
129
+ JOIN pg_catalog.pg_class cls ON cls.oid = att.attrelid
130
+ JOIN pg_catalog.pg_namespace ns ON ns.oid = cls.relnamespace
131
+ WHERE ns.nspname = $1
132
+ AND att.attnum > 0
133
+ AND NOT att.attisdropped
134
+ `,
135
+ [schema]
136
+ )) as ColumnCommentRow[];
137
+ const columnComments = new Map<string, string>();
138
+ columnCommentRows.forEach(r => {
139
+ if (!shouldIncludeTable(r.table_name, options)) return;
140
+ if (!r.description) return;
141
+ const key = `${r.table_schema}.${r.table_name}.${r.column_name}`;
142
+ const trimmed = r.description.trim();
143
+ if (!trimmed) return;
144
+ columnComments.set(key, trimmed);
145
+ });
146
+ const tableCommentRows = (await queryRows(
147
+ ctx.executor,
148
+ `
149
+ SELECT
150
+ ns.nspname AS table_schema,
151
+ cls.relname AS table_name,
152
+ pg_catalog.obj_description(cls.oid) AS description
153
+ FROM pg_catalog.pg_class cls
154
+ JOIN pg_catalog.pg_namespace ns ON ns.oid = cls.relnamespace
155
+ WHERE ns.nspname = $1
156
+ AND cls.relkind IN ('r', 'p')
157
+ `,
158
+ [schema]
159
+ )) as TableCommentRow[];
160
+ const tableComments = new Map<string, string>();
161
+ tableCommentRows.forEach(r => {
162
+ if (!shouldIncludeTable(r.table_name, options)) return;
163
+ if (!r.description) return;
164
+ const key = `${r.table_schema}.${r.table_name}`;
165
+ const trimmed = r.description.trim();
166
+ if (!trimmed) return;
167
+ tableComments.set(key, trimmed);
168
+ });
140
169
 
141
170
  // Primary key columns query
142
171
  const qbPk = new SelectQueryBuilder(PgKeyColumnUsage)
@@ -303,13 +332,14 @@ export const postgresIntrospector: SchemaIntrospector = {
303
332
  return;
304
333
  }
305
334
  if (!tablesByKey.has(key)) {
306
- tablesByKey.set(key, {
307
- name: r.table_name,
308
- schema: r.table_schema,
309
- columns: [],
310
- primaryKey: pkMap.get(key) || [],
311
- indexes: []
312
- });
335
+ tablesByKey.set(key, {
336
+ name: r.table_name,
337
+ schema: r.table_schema,
338
+ columns: [],
339
+ primaryKey: pkMap.get(key) || [],
340
+ indexes: [],
341
+ comment: tableComments.get(key)
342
+ });
313
343
  }
314
344
  const cols = tablesByKey.get(key)!;
315
345
  const commentKey = `${r.table_schema}.${r.table_name}.${r.column_name}`;
@@ -1,6 +1,6 @@
1
1
  import { SchemaIntrospector, IntrospectOptions } from './types.js';
2
- import { shouldIncludeTable } from './utils.js';
3
- import { DatabaseSchema, DatabaseTable, DatabaseIndex } from '../schema-types.js';
2
+ import { shouldIncludeTable, queryRows } from './utils.js';
3
+ import { DatabaseSchema, DatabaseTable, DatabaseIndex, DatabaseColumn } from '../schema-types.js';
4
4
  import type { IntrospectContext } from './context.js';
5
5
  import { runSelectNode } from './run-select.js';
6
6
  import type { SelectQueryNode, TableNode } from '../../ast/query.js';
@@ -89,6 +89,58 @@ const runPragma = async <T>(
89
89
  return (await runSelectNode<T>(query, ctx)) as T[];
90
90
  };
91
91
 
92
+ const loadSqliteSchemaComments = async (ctx: IntrospectContext) => {
93
+ const tableComments = new Map<string, string>();
94
+ const columnComments = new Map<string, string>();
95
+ const tableExists = await queryRows(
96
+ ctx.executor,
97
+ `SELECT name FROM sqlite_master WHERE type='table' AND name='schema_comments' LIMIT 1`
98
+ );
99
+ if (!tableExists.length) {
100
+ return { tableComments, columnComments };
101
+ }
102
+
103
+ const commentRows = await queryRows(
104
+ ctx.executor,
105
+ `SELECT object_type, schema_name, table_name, column_name, comment FROM schema_comments`
106
+ );
107
+ for (const row of commentRows) {
108
+ const objectType = typeof row.object_type === 'string' ? row.object_type.toLowerCase() : '';
109
+ const tableName = typeof row.table_name === 'string' ? row.table_name : '';
110
+ if (!tableName) continue;
111
+ const columnName = typeof row.column_name === 'string' ? row.column_name : '';
112
+ const schemaName = typeof row.schema_name === 'string' ? row.schema_name : '';
113
+ const rawComment = row.comment;
114
+ if (rawComment == null) continue;
115
+ const commentText = String(rawComment).trim();
116
+ if (!commentText) continue;
117
+
118
+ const addTableComment = () => {
119
+ tableComments.set(tableName, commentText);
120
+ if (schemaName) {
121
+ tableComments.set(`${schemaName}.${tableName}`, commentText);
122
+ }
123
+ };
124
+ const addColumnComment = () => {
125
+ columnComments.set(`${tableName}.${columnName}`, commentText);
126
+ if (schemaName) {
127
+ columnComments.set(`${schemaName}.${tableName}.${columnName}`, commentText);
128
+ }
129
+ };
130
+
131
+ if (objectType === 'table') {
132
+ addTableComment();
133
+ } else if (objectType === 'column' && columnName) {
134
+ addColumnComment();
135
+ }
136
+ }
137
+
138
+ return {
139
+ tableComments,
140
+ columnComments
141
+ };
142
+ };
143
+
92
144
  export const sqliteIntrospector: SchemaIntrospector = {
93
145
  async introspect(ctx: IntrospectContext, options: IntrospectOptions): Promise<DatabaseSchema> {
94
146
  const alias = 'sqlite_master';
@@ -103,6 +155,7 @@ export const sqliteIntrospector: SchemaIntrospector = {
103
155
  )
104
156
  };
105
157
 
158
+ const { tableComments, columnComments } = await loadSqliteSchemaComments(ctx);
106
159
  const tableRows = (await runSelectNode<SqliteTableRow>(tablesQuery, ctx)) as SqliteTableRow[];
107
160
  const tables: DatabaseTable[] = [];
108
161
 
@@ -134,16 +187,27 @@ export const sqliteIntrospector: SchemaIntrospector = {
134
187
  ctx
135
188
  );
136
189
 
137
- const tableEntry: DatabaseTable = { name: tableName, columns: [], primaryKey: [], indexes: [] };
190
+ const tableEntry: DatabaseTable = {
191
+ name: tableName,
192
+ columns: [],
193
+ primaryKey: [],
194
+ indexes: [],
195
+ comment: tableComments.get(tableName)
196
+ };
138
197
 
139
198
  tableInfo.forEach(info => {
140
- tableEntry.columns.push({
199
+ const column: DatabaseColumn = {
141
200
  name: info.name,
142
201
  type: info.type,
143
202
  notNull: info.notnull === 1,
144
203
  default: info.dflt_value ?? undefined,
145
204
  autoIncrement: false
146
- });
205
+ };
206
+ const columnComment = columnComments.get(`${tableName}.${info.name}`);
207
+ if (columnComment) {
208
+ column.comment = columnComment;
209
+ }
210
+ tableEntry.columns.push(column);
147
211
  if (info.pk && info.pk > 0) {
148
212
  tableEntry.primaryKey = tableEntry.primaryKey || [];
149
213
  tableEntry.primaryKey.push(info.name);
@@ -134,15 +134,20 @@ export const col = {
134
134
 
135
135
  /**
136
136
  * Creates a variable character column definition
137
- * @param length - Maximum length of the string
138
- * @returns ColumnDef with VARCHAR type
139
- */
140
- varchar: (length: number): ColumnDef<'VARCHAR'> => ({ name: '', type: 'VARCHAR', args: [length] }),
141
-
142
- /**
143
- * Creates a fixed precision decimal column definition
144
- */
145
- decimal: (precision: number, scale = 0): ColumnDef<'DECIMAL'> => ({
137
+ * @param length - Maximum length of the string
138
+ * @returns ColumnDef with VARCHAR type
139
+ */
140
+ varchar: (length: number): ColumnDef<'VARCHAR'> => ({ name: '', type: 'VARCHAR', args: [length] }),
141
+
142
+ /**
143
+ * Creates a text column definition
144
+ */
145
+ text: (): ColumnDef<'TEXT'> => ({ name: '', type: 'TEXT' }),
146
+
147
+ /**
148
+ * Creates a fixed precision decimal column definition
149
+ */
150
+ decimal: (precision: number, scale = 0): ColumnDef<'DECIMAL'> => ({
146
151
  name: '',
147
152
  type: 'DECIMAL',
148
153
  args: [precision, scale]