UNPKG

plugin-postgresql-connector

Version:

NocoBase plugin for connecting to external PostgreSQL databases

444 lines 17.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SchemaService = void 0; class SchemaService { constructor(connectionManager) { this.connectionManager = connectionManager; } async getDatabaseInfo(connectionId) { const client = await this.connectionManager.getConnection(connectionId); try { // Get basic database info const dbInfoQuery = ` SELECT current_database() as database_name, pg_encoding_to_char(encoding) as encoding, datcollate as collation FROM pg_database WHERE datname = current_database() `; const dbInfoResult = await client.query(dbInfoQuery); const dbInfo = dbInfoResult.rows[0]; // Get database size const sizeQuery = `SELECT pg_size_pretty(pg_database_size(current_database())) as database_size`; const sizeResult = await client.query(sizeQuery); // Get PostgreSQL version const versionQuery = `SELECT version() as version`; const versionResult = await client.query(versionQuery); // Get connection count const connectionsQuery = ` SELECT count(*) as connection_count FROM pg_stat_activity WHERE datname = current_database() `; const connectionsResult = await client.query(connectionsQuery); // Get uptime (PostgreSQL start time) const uptimeQuery = ` SELECT date_trunc('second', now() - pg_postmaster_start_time()) as uptime `; const uptimeResult = await client.query(uptimeQuery); return { database_name: dbInfo.database_name, database_size: sizeResult.rows[0].database_size, encoding: dbInfo.encoding, collation: dbInfo.collation, connection_count: parseInt(connectionsResult.rows[0].connection_count), version: versionResult.rows[0].version, uptime: uptimeResult.rows[0].uptime, }; } finally { client.release(); } } async getSchemaStatistics(connectionId, schema = 'public') { const client = await this.connectionManager.getConnection(connectionId); try { // Get table count const tablesQuery = ` SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE' `; const tablesResult = await client.query(tablesQuery, [schema]); // Get view count const viewsQuery = ` SELECT COUNT(*) as count FROM information_schema.views WHERE table_schema = $1 `; const viewsResult = await client.query(viewsQuery, [schema]); // Get function count const functionsQuery = ` SELECT COUNT(*) as count FROM information_schema.routines WHERE routine_schema = $1 AND routine_type = 'FUNCTION' `; const functionsResult = await client.query(functionsQuery, [schema]); // Get procedure count const proceduresQuery = ` SELECT COUNT(*) as count FROM information_schema.routines WHERE routine_schema = $1 AND routine_type = 'PROCEDURE' `; const proceduresResult = await client.query(proceduresQuery, [schema]); // Get index count const indexesQuery = ` SELECT COUNT(*) as count FROM pg_indexes WHERE schemaname = $1 `; const indexesResult = await client.query(indexesQuery, [schema]); // Get database size const sizeQuery = `SELECT pg_size_pretty(pg_database_size(current_database())) as database_size`; const sizeResult = await client.query(sizeQuery); // Get all schemas const schemasQuery = ` SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema', 'pg_catalog', 'pg_toast') ORDER BY schema_name `; const schemasResult = await client.query(schemasQuery); return { total_tables: parseInt(tablesResult.rows[0].count), total_views: parseInt(viewsResult.rows[0].count), total_functions: parseInt(functionsResult.rows[0].count), total_procedures: parseInt(proceduresResult.rows[0].count), total_indexes: parseInt(indexesResult.rows[0].count), database_size: sizeResult.rows[0].database_size, schemas: schemasResult.rows.map(row => row.schema_name), }; } finally { client.release(); } } async getTables(connectionId, schema = 'public') { const client = await this.connectionManager.getConnection(connectionId); try { const query = ` SELECT t.table_name, t.table_schema, t.table_type, obj_description(c.oid) as table_comment, ( SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = t.table_schema AND table_name = t.table_name ) as column_count, ( SELECT reltuples::bigint FROM pg_class c2 JOIN pg_namespace n ON c2.relnamespace = n.oid WHERE n.nspname = t.table_schema AND c2.relname = t.table_name ) as row_count FROM information_schema.tables t LEFT JOIN pg_class c ON c.relname = t.table_name LEFT JOIN pg_namespace n ON c.relnamespace = n.oid AND n.nspname = t.table_schema WHERE t.table_schema = $1 AND t.table_type = 'BASE TABLE' ORDER BY t.table_name `; const result = await client.query(query, [schema]); return result.rows; } finally { client.release(); } } async getTableColumns(connectionId, tableName, schema = 'public') { const client = await this.connectionManager.getConnection(connectionId); try { const query = ` SELECT c.column_name, c.data_type, c.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 pk.column_name IS NOT NULL THEN true ELSE false END as is_primary_key, CASE WHEN fk.column_name IS NOT NULL THEN true ELSE false END as is_foreign_key, fk.foreign_table_name as foreign_key_table, fk.foreign_column_name as foreign_key_column FROM information_schema.columns c LEFT JOIN pg_class pgc ON pgc.relname = c.table_name LEFT JOIN pg_namespace pgn ON pgc.relnamespace = pgn.oid AND pgn.nspname = c.table_schema LEFT JOIN ( SELECT kcu.column_name, kcu.table_name, kcu.table_schema 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' ) pk ON pk.column_name = c.column_name AND pk.table_name = c.table_name AND pk.table_schema = c.table_schema LEFT JOIN ( SELECT kcu.column_name, kcu.table_name, kcu.table_schema, ccu.table_name as foreign_table_name, ccu.column_name as foreign_column_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name WHERE tc.constraint_type = 'FOREIGN KEY' ) fk ON fk.column_name = c.column_name AND fk.table_name = c.table_name AND fk.table_schema = c.table_schema WHERE c.table_name = $1 AND c.table_schema = $2 ORDER BY c.ordinal_position `; const result = await client.query(query, [tableName, schema]); return result.rows; } finally { client.release(); } } async getViews(connectionId, schema = 'public') { const client = await this.connectionManager.getConnection(connectionId); try { const query = ` SELECT table_name as view_name, table_schema as view_schema, view_definition, is_updatable, check_option FROM information_schema.views WHERE table_schema = $1 ORDER BY table_name `; const result = await client.query(query, [schema]); return result.rows; } finally { client.release(); } } async getFunctions(connectionId, schema = 'public') { const client = await this.connectionManager.getConnection(connectionId); try { const query = ` SELECT r.routine_name as function_name, r.routine_schema as function_schema, r.routine_type, r.data_type, r.routine_definition, r.external_language, ( SELECT COUNT(*) FROM information_schema.parameters p WHERE p.specific_name = r.specific_name ) as parameter_count FROM information_schema.routines r WHERE r.routine_schema = $1 ORDER BY r.routine_name, r.routine_type `; const result = await client.query(query, [schema]); // Get parameters for each function for (const func of result.rows) { const paramsQuery = ` SELECT parameter_name, data_type, parameter_mode FROM information_schema.parameters WHERE specific_name = ( SELECT specific_name FROM information_schema.routines WHERE routine_name = $1 AND routine_schema = $2 AND routine_type = $3 ) ORDER BY ordinal_position `; const paramsResult = await client.query(paramsQuery, [ func.function_name, func.function_schema, func.routine_type ]); func.parameters = paramsResult.rows; } return result.rows; } finally { client.release(); } } async getIndexes(connectionId, tableName, schema = 'public') { const client = await this.connectionManager.getConnection(connectionId); try { let query = ` SELECT i.indexname as index_name, i.tablename as table_name, array_agg(a.attname ORDER BY a.attnum) as column_names, idx.indisunique as is_unique, idx.indisprimary as is_primary, am.amname as index_type FROM pg_indexes i JOIN pg_class c ON c.relname = i.indexname JOIN pg_index idx ON idx.indexrelid = c.oid JOIN pg_class t ON t.oid = idx.indrelid JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(idx.indkey) JOIN pg_am am ON am.oid = c.relam WHERE i.schemaname = $1 `; const params = [schema]; if (tableName) { query += ` AND i.tablename = $2`; params.push(tableName); } query += ` GROUP BY i.indexname, i.tablename, idx.indisunique, idx.indisprimary, am.amname ORDER BY i.tablename, i.indexname `; const result = await client.query(query, params); return result.rows; } finally { client.release(); } } async searchObjects(connectionId, searchTerm, objectTypes = ['table', 'view', 'function'], schema = 'public') { const client = await this.connectionManager.getConnection(connectionId); try { const results = []; const searchPattern = `%${searchTerm.toLowerCase()}%`; // Search tables if (objectTypes.includes('table')) { const tablesQuery = ` SELECT table_name as name, 'table' as type, table_schema as schema FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE' AND LOWER(table_name) LIKE $2 `; const tablesResult = await client.query(tablesQuery, [schema, searchPattern]); results.push(...tablesResult.rows); } // Search views if (objectTypes.includes('view')) { const viewsQuery = ` SELECT table_name as name, 'view' as type, table_schema as schema FROM information_schema.views WHERE table_schema = $1 AND LOWER(table_name) LIKE $2 `; const viewsResult = await client.query(viewsQuery, [schema, searchPattern]); results.push(...viewsResult.rows); } // Search functions if (objectTypes.includes('function')) { const functionsQuery = ` SELECT routine_name as name, routine_type as type, routine_schema as schema FROM information_schema.routines WHERE routine_schema = $1 AND LOWER(routine_name) LIKE $2 `; const functionsResult = await client.query(functionsQuery, [schema, searchPattern]); results.push(...functionsResult.rows); } return results.sort((a, b) => a.name.localeCompare(b.name)); } finally { client.release(); } } async getTableRelationships(connectionId, tableName, schema = 'public') { const client = await this.connectionManager.getConnection(connectionId); try { const query = ` SELECT tc.constraint_name, tc.constraint_type, kcu.column_name, ccu.table_name as foreign_table_name, ccu.column_name as foreign_column_name, rc.update_rule, rc.delete_rule FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name LEFT JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name LEFT JOIN information_schema.referential_constraints rc ON tc.constraint_name = rc.constraint_name WHERE tc.table_name = $1 AND tc.table_schema = $2 ORDER BY tc.constraint_type, kcu.ordinal_position `; const result = await client.query(query, [tableName, schema]); return result.rows; } finally { client.release(); } } /** * Get table size and statistics */ async getTableStatistics(connectionId, tableName, schema = 'public') { const client = await this.connectionManager.getConnection(connectionId); try { const query = ` SELECT schemaname, tablename, attname, n_distinct, most_common_vals, most_common_freqs, histogram_bounds, correlation, most_common_elems, most_common_elem_freqs, elem_count_histogram FROM pg_stats WHERE schemaname = $1 AND tablename = $2 ORDER BY attname `; const statsResult = await client.query(query, [schema, tableName]); // Get table size information const sizeQuery = ` SELECT pg_size_pretty(pg_total_relation_size($1::regclass)) as total_size, pg_size_pretty(pg_relation_size($1::regclass)) as table_size, pg_size_pretty(pg_total_relation_size($1::regclass) - pg_relation_size($1::regclass)) as index_size `; const sizeResult = await client.query(sizeQuery, [`${schema}.${tableName}`]); return { statistics: statsResult.rows, size: sizeResult.rows[0], }; } finally { client.release(); } } } exports.SchemaService = SchemaService; exports.default = SchemaService; //# sourceMappingURL=SchemaService.js.map