metal-orm 1.0.49 → 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.
- package/dist/index.cjs +151 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +151 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/generate-entities/cli.mjs +101 -0
- package/scripts/generate-entities/render.mjs +130 -17
- package/scripts/inflection/compound.mjs +72 -0
- package/scripts/inflection/en.mjs +26 -0
- package/scripts/inflection/index.mjs +29 -0
- package/scripts/inflection/pt-br.mjs +391 -0
- package/scripts/naming-strategy.mjs +27 -63
- package/scripts/pt-pluralizer.mjs +19 -0
- package/src/core/ddl/introspect/mssql.ts +74 -2
- package/src/core/ddl/introspect/postgres.ts +69 -39
- package/src/core/ddl/introspect/sqlite.ts +69 -5
- package/src/schema/column-types.ts +14 -9
|
@@ -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 = {
|
|
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
|
-
|
|
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
|
|
144
|
-
*/
|
|
145
|
-
|
|
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]
|