UNPKG

nestjs-reverse-engineering

Version:

A powerful TypeScript/NestJS library for database reverse engineering, entity generation, and CRUD operations

202 lines 8.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PostgresSchemaIntrospector = exports.BaseSchemaIntrospector = void 0; const database_types_1 = require("../types/database.types"); class BaseSchemaIntrospector { constructor(dataSource) { this.dataSource = dataSource; } async getDatabaseSchema() { const tables = await this.getAllTables(); return { dialect: this.getDialect(), tables }; } } exports.BaseSchemaIntrospector = BaseSchemaIntrospector; class PostgresSchemaIntrospector extends BaseSchemaIntrospector { getDialect() { return database_types_1.DatabaseDialect.POSTGRES; } async getAllTables() { const query = ` SELECT t.table_name, t.table_schema, obj_description(c.oid) as table_comment FROM information_schema.tables t LEFT JOIN pg_class c ON c.relname = t.table_name LEFT JOIN pg_namespace n ON n.oid = c.relnamespace AND n.nspname = t.table_schema WHERE t.table_type = 'BASE TABLE' AND t.table_schema NOT IN ('information_schema', 'pg_catalog', 'pg_toast') ORDER BY t.table_schema, t.table_name; `; const tables = await this.dataSource.query(query); const tableInfos = []; for (const table of tables) { const tableInfo = await this.getTableInfo(table.table_name, table.table_schema); tableInfos.push(tableInfo); } return tableInfos; } async getTableInfo(tableName, schema = 'public') { const [columns, primaryKeys, foreignKeys, indexes] = await Promise.all([ this.getColumns(tableName, schema), this.getPrimaryKeys(tableName, schema), this.getForeignKeys(tableName, schema), this.getIndexes(tableName, schema) ]); const tableCommentQuery = ` SELECT obj_description(c.oid) as table_comment FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = $1 AND n.nspname = $2; `; const commentResult = await this.dataSource.query(tableCommentQuery, [tableName, schema]); const tableComment = commentResult[0]?.table_comment; return { tableName, tableSchema: schema, tableComment, columns, primaryKeys, foreignKeys, indexes }; } async getColumns(tableName, schema) { const query = ` SELECT c.column_name, c.data_type, c.is_nullable::boolean as is_nullable, c.column_default, c.character_maximum_length, c.numeric_precision, c.numeric_scale, c.ordinal_position, col_description(pgc.oid, c.ordinal_position) as column_comment, CASE WHEN c.column_default LIKE 'nextval%' THEN true ELSE false END as is_auto_increment, CASE WHEN c.data_type = 'USER-DEFINED' THEN c.udt_name ELSE c.data_type END as actual_data_type, CASE WHEN c.data_type = 'ARRAY' THEN CASE WHEN c.udt_name LIKE '%[]' THEN substring(c.udt_name from 1 for length(c.udt_name)-2) || '[]' ELSE c.udt_name END ELSE c.data_type END as processed_data_type FROM information_schema.columns c LEFT JOIN pg_class pgc ON pgc.relname = c.table_name LEFT JOIN pg_namespace pgn ON pgn.oid = pgc.relnamespace AND pgn.nspname = c.table_schema WHERE c.table_name = $1 AND c.table_schema = $2 ORDER BY c.ordinal_position; `; const columns = await this.dataSource.query(query, [tableName, schema]); const columnInfos = []; for (const col of columns) { const enumValues = col.actual_data_type !== col.data_type ? await this.getEnumValues(col.actual_data_type, schema) : undefined; columnInfos.push({ columnName: col.column_name, dataType: col.processed_data_type || col.actual_data_type, isNullable: col.is_nullable === 'YES' || col.is_nullable === true, defaultValue: col.column_default, characterMaximumLength: col.character_maximum_length, numericPrecision: col.numeric_precision, numericScale: col.numeric_scale, columnComment: col.column_comment, isAutoIncrement: col.is_auto_increment, ordinalPosition: col.ordinal_position, enumValues }); } return columnInfos; } async getEnumValues(enumName, schema) { try { const query = ` SELECT e.enumlabel FROM pg_enum e JOIN pg_type t ON e.enumtypid = t.oid JOIN pg_namespace n ON t.typnamespace = n.oid WHERE t.typname = $1 AND n.nspname = $2 ORDER BY e.enumsortorder; `; const result = await this.dataSource.query(query, [enumName, schema]); return result.map((row) => row.enumlabel); } catch { return undefined; } } async getPrimaryKeys(tableName, schema) { const query = ` SELECT kcu.column_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type = 'PRIMARY KEY' AND tc.table_name = $1 AND tc.table_schema = $2 ORDER BY kcu.ordinal_position; `; const result = await this.dataSource.query(query, [tableName, schema]); return result.map((row) => row.column_name); } async getForeignKeys(tableName, schema) { const query = ` SELECT tc.constraint_name, kcu.column_name, ccu.table_schema AS referenced_table_schema, ccu.table_name AS referenced_table_name, ccu.column_name AS referenced_column_name, rc.delete_rule as on_delete, rc.update_rule as on_update FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name JOIN information_schema.referential_constraints AS rc ON rc.constraint_name = tc.constraint_name WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name = $1 AND tc.table_schema = $2; `; const result = await this.dataSource.query(query, [tableName, schema]); return result.map((row) => ({ constraintName: row.constraint_name, columnName: row.column_name, referencedTableSchema: row.referenced_table_schema, referencedTableName: row.referenced_table_name, referencedColumnName: row.referenced_column_name, onDelete: row.on_delete, onUpdate: row.on_update })); } async getIndexes(tableName, schema) { const query = ` SELECT i.relname as index_name, array_agg(a.attname ORDER BY c.ordinality) as column_names, ix.indisunique as is_unique, ix.indisprimary as is_primary FROM pg_class t JOIN pg_namespace n ON t.relnamespace = n.oid JOIN pg_index ix ON t.oid = ix.indrelid JOIN pg_class i ON i.oid = ix.indexrelid JOIN unnest(ix.indkey) WITH ORDINALITY AS c(attnum, ordinality) ON true JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = c.attnum WHERE t.relname = $1 AND n.nspname = $2 GROUP BY i.relname, ix.indisunique, ix.indisprimary ORDER BY i.relname; `; const result = await this.dataSource.query(query, [tableName, schema]); return result.map((row) => ({ indexName: row.index_name, columnNames: row.column_names, isUnique: row.is_unique, isPrimary: row.is_primary })); } } exports.PostgresSchemaIntrospector = PostgresSchemaIntrospector; //# sourceMappingURL=postgres-introspector.js.map