diff --git a/plugins/dbgate-plugin-postgres/src/backend/drivers.js b/plugins/dbgate-plugin-postgres/src/backend/drivers.js index 595b56b97..de81e3d19 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/drivers.js +++ b/plugins/dbgate-plugin-postgres/src/backend/drivers.js @@ -6,6 +6,7 @@ const Analyser = require('./Analyser'); const wkx = require('wkx'); const pg = require('pg'); const pgCopyStreams = require('pg-copy-streams'); +const sql = require('./sql'); const { getLogger, createBulkInsertStreamBase, @@ -351,11 +352,65 @@ const drivers = driverBases.map(driverBase => ({ // @ts-ignore return createBulkInsertStreamBase(this, stream, dbhan, name, options); }, + + async serverSummary(dbhan) { + const [processes, variables, databases] = await Promise.all([ + this.listProcesses(dbhan), + this.listVariables(dbhan), + this.listDatabases(dbhan), + ]); + + /** @type {import('dbgate-types').ServerSummary} */ + const data = { + processes, + variables, + databases: { + rows: databases, + columns: [ + { header: 'Name', fieldName: 'name', type: 'data' }, + { header: 'Size on disk', fieldName: 'sizeOnDisk', type: 'fileSize' }, + ], + }, + }; + + return data; + }, + + async killProcess(dbhan, pid) { + const result = await this.query(dbhan, `SELECT pg_terminate_backend(${parseInt(pid)})`); + return result; + }, + async listDatabases(dbhan) { - const { rows } = await this.query(dbhan, 'SELECT datname AS name FROM pg_database WHERE datistemplate = false'); + const { rows } = await this.query(dbhan, sql.listDatabases); return rows; }, + async listVariables(dbhan) { + const result = await this.query(dbhan, sql.listVariables); + return result.rows.map(row => ({ + variable: row.variable, + value: row.value, + })); + }, + + async listProcesses(dbhan) { + const result = await this.query(dbhan, sql.listProcesses); + return result.rows.map(row => ({ + processId: row.processId, + connectionId: row.connectionId, + client: row.client, + operation: row.operation, + namespace: null, + command: row.operation, + runningTime: row.runningTime ? Math.max(Number(row.runningTime), 0) : null, + state: row.state, + waitingFor: row.waitingFor, + locks: null, + progress: null, + })); + }, + getAuthTypes() { const res = [ { diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/index.js b/plugins/dbgate-plugin-postgres/src/backend/sql/index.js index 8f648c64f..833153196 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/sql/index.js +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/index.js @@ -17,6 +17,9 @@ const geographyColumns = require('./geographyColumns'); const proceduresParameters = require('./proceduresParameters'); const foreignKeys = require('./foreignKeys'); const triggers = require('./triggers'); +const listDatabases = require('./listDatabases'); +const listVariables = require('./listVariables'); +const listProcesses = require('./listProcesses'); const fk_keyColumnUsage = require('./fk_key_column_usage'); @@ -41,4 +44,7 @@ module.exports = { geographyColumns, proceduresParameters, triggers, + listDatabases, + listVariables, + listProcesses, }; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/listDatabases.js b/plugins/dbgate-plugin-postgres/src/backend/sql/listDatabases.js new file mode 100644 index 000000000..a033b9297 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/listDatabases.js @@ -0,0 +1,11 @@ +module.exports = ` +SELECT + "datname" AS "name", + pg_database_size("datname") AS "sizeOnDisk", + 0 AS "tableCount", + 0 AS "viewCount", + 0 AS "matviewCount" +FROM "pg_database" +WHERE "datistemplate" = false +ORDER BY pg_database_size("datname") DESC +`; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/listProcesses.js b/plugins/dbgate-plugin-postgres/src/backend/sql/listProcesses.js new file mode 100644 index 000000000..807dcad38 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/listProcesses.js @@ -0,0 +1,13 @@ +module.exports = ` +SELECT + "pid" AS "processId", + "application_name" AS "client", + "client_addr" AS "connectionId", + "state" AS "state", + "query" AS "operation", + EXTRACT(EPOCH FROM (NOW() - "state_change")) AS "runningTime", + "wait_event" IS NOT NULL AS "waitingFor" +FROM "pg_stat_activity" +WHERE "state" IS NOT NULL +ORDER BY "pid" +`; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/listVariables.js b/plugins/dbgate-plugin-postgres/src/backend/sql/listVariables.js new file mode 100644 index 000000000..0973f035b --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/listVariables.js @@ -0,0 +1,5 @@ +module.exports = ` +SELECT "name" AS "variable", "setting" AS "value" +FROM "pg_settings" +ORDER BY "name" +`; diff --git a/plugins/dbgate-plugin-postgres/src/frontend/drivers.js b/plugins/dbgate-plugin-postgres/src/frontend/drivers.js index e44bf8b6f..c67f1577e 100644 --- a/plugins/dbgate-plugin-postgres/src/frontend/drivers.js +++ b/plugins/dbgate-plugin-postgres/src/frontend/drivers.js @@ -361,6 +361,7 @@ EXECUTE FUNCTION function_name();`, /** @type {import('dbgate-types').EngineDriver} */ const postgresDriver = { ...postgresDriverBase, + supportsServerSummary: true, engine: 'postgres@dbgate-plugin-postgres', title: 'PostgreSQL', defaultPort: 5432, @@ -388,6 +389,7 @@ const postgresDriver = { /** @type {import('dbgate-types').EngineDriver} */ const cockroachDriver = { ...postgresDriverBase, + supportsServerSummary: true, engine: 'cockroach@dbgate-plugin-postgres', title: 'CockroachDB', defaultPort: 26257, @@ -403,6 +405,7 @@ const cockroachDriver = { /** @type {import('dbgate-types').EngineDriver} */ const redshiftDriver = { ...postgresDriverBase, + supportsServerSummary: true, dialect: { ...dialect, stringAgg: false,