@hesed/psql 0.2.2 → 0.3.0
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/README.md +107 -32
- package/dist/commands/psql/auth/add.d.ts +2 -18
- package/dist/commands/psql/auth/add.js +16 -57
- package/dist/commands/psql/auth/delete.d.ts +2 -0
- package/dist/commands/psql/auth/delete.js +2 -0
- package/dist/commands/psql/auth/list.d.ts +2 -0
- package/dist/commands/psql/auth/list.js +2 -0
- package/dist/commands/psql/auth/profile.d.ts +2 -0
- package/dist/commands/psql/auth/profile.js +2 -0
- package/dist/commands/psql/auth/test.d.ts +2 -12
- package/dist/commands/psql/auth/test.js +16 -41
- package/dist/commands/psql/auth/update.d.ts +2 -18
- package/dist/commands/psql/auth/update.js +16 -74
- package/dist/commands/psql/databases.js +2 -10
- package/dist/commands/psql/describe-table.js +2 -10
- package/dist/commands/psql/explain-query.js +2 -10
- package/dist/commands/psql/indexes.js +2 -10
- package/dist/commands/psql/query.js +2 -10
- package/dist/commands/psql/tables.js +2 -10
- package/dist/psql/config-loader.d.ts +8 -14
- package/dist/psql/config-loader.js +0 -7
- package/dist/psql/formatters.d.ts +8 -0
- package/dist/psql/formatters.js +76 -0
- package/dist/psql/index.d.ts +1 -2
- package/dist/psql/index.js +1 -1
- package/dist/psql/postgres-client.d.ts +8 -57
- package/dist/psql/postgres-client.js +44 -101
- package/dist/psql/postgres-utils.d.ts +1 -55
- package/dist/psql/postgres-utils.js +8 -168
- package/dist/psql/query-validator.d.ts +0 -19
- package/dist/psql/query-validator.js +13 -20
- package/oclif.manifest.json +176 -59
- package/package.json +2 -1
- package/dist/config.d.ts +0 -13
- package/dist/config.js +0 -18
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
-
import { closeConnections,
|
|
2
|
+
import { closeConnections, showIndexes } from '../../psql/index.js';
|
|
3
3
|
export default class PostgresIndexes extends Command {
|
|
4
4
|
static args = {
|
|
5
5
|
table: Args.string({ description: 'Table name to show indexes for', required: true }),
|
|
@@ -19,15 +19,7 @@ export default class PostgresIndexes extends Command {
|
|
|
19
19
|
};
|
|
20
20
|
async run() {
|
|
21
21
|
const { args, flags } = await this.parse(PostgresIndexes);
|
|
22
|
-
|
|
23
|
-
let profile;
|
|
24
|
-
try {
|
|
25
|
-
profile = flags.profile ?? (await getPgConfig()).defaultProfile;
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
this.error(error instanceof Error ? error.message : String(error));
|
|
29
|
-
}
|
|
30
|
-
const result = await showIndexes(profile, args.table, flags.format);
|
|
22
|
+
const result = await showIndexes(this.config, args.table, flags.profile, flags.format);
|
|
31
23
|
await closeConnections();
|
|
32
24
|
if (result.success) {
|
|
33
25
|
this.log(result.result ?? '');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
-
import { closeConnections, executeQuery
|
|
2
|
+
import { closeConnections, executeQuery } from '../../psql/index.js';
|
|
3
3
|
export default class PostgresQuery extends Command {
|
|
4
4
|
static args = {
|
|
5
5
|
query: Args.string({ description: 'SQL query to execute', required: true }),
|
|
@@ -24,15 +24,7 @@ export default class PostgresQuery extends Command {
|
|
|
24
24
|
};
|
|
25
25
|
async run() {
|
|
26
26
|
const { args, flags } = await this.parse(PostgresQuery);
|
|
27
|
-
|
|
28
|
-
let profile;
|
|
29
|
-
try {
|
|
30
|
-
profile = flags.profile ?? (await getPgConfig()).defaultProfile;
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
this.error(error instanceof Error ? error.message : String(error));
|
|
34
|
-
}
|
|
35
|
-
const result = await executeQuery(args.query, profile, flags.format, flags['skip-confirmation']);
|
|
27
|
+
const result = await executeQuery(this.config, args.query, flags.profile, flags.format, flags['skip-confirmation']);
|
|
36
28
|
await closeConnections();
|
|
37
29
|
if (result.success) {
|
|
38
30
|
this.log(result.result ?? '');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
|
-
import { closeConnections,
|
|
2
|
+
import { closeConnections, listTables } from '../../psql/index.js';
|
|
3
3
|
export default class PostgresTables extends Command {
|
|
4
4
|
static description = 'List all tables in the current PostgreSQL database';
|
|
5
5
|
static examples = [
|
|
@@ -11,15 +11,7 @@ export default class PostgresTables extends Command {
|
|
|
11
11
|
};
|
|
12
12
|
async run() {
|
|
13
13
|
const { flags } = await this.parse(PostgresTables);
|
|
14
|
-
|
|
15
|
-
let profile;
|
|
16
|
-
try {
|
|
17
|
-
profile = flags.profile ?? (await getPgConfig()).defaultProfile;
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
this.error(error instanceof Error ? error.message : String(error));
|
|
21
|
-
}
|
|
22
|
-
const result = await listTables(profile);
|
|
14
|
+
const result = await listTables(this.config, flags.profile);
|
|
23
15
|
await closeConnections();
|
|
24
16
|
if (result.success) {
|
|
25
17
|
this.logJson(result.tables);
|
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
import type { ClientConfig } from 'pg';
|
|
2
|
-
import type { DatabaseProfile } from '../config.js';
|
|
3
|
-
/**
|
|
4
|
-
* Safety configuration for query execution
|
|
5
|
-
*/
|
|
6
2
|
interface SafetyConfig {
|
|
7
3
|
blacklistedOperations: string[];
|
|
8
4
|
defaultLimit: number;
|
|
9
5
|
requireConfirmationFor: string[];
|
|
10
6
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
export interface DatabaseProfile {
|
|
8
|
+
database: string;
|
|
9
|
+
host: string;
|
|
10
|
+
password: string;
|
|
11
|
+
port: number;
|
|
12
|
+
ssl?: boolean;
|
|
13
|
+
user: string;
|
|
14
|
+
}
|
|
14
15
|
export interface PgConfig {
|
|
15
16
|
defaultFormat: 'csv' | 'json' | 'table' | 'toon';
|
|
16
17
|
defaultProfile: string;
|
|
17
18
|
profiles: Record<string, DatabaseProfile>;
|
|
18
19
|
safety: SafetyConfig;
|
|
19
20
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Get PostgreSQL connection options for a specific profile
|
|
22
|
-
*
|
|
23
|
-
* @param config - Configuration object
|
|
24
|
-
* @param profileName - Profile name
|
|
25
|
-
* @returns pg ClientConfig options
|
|
26
|
-
*/
|
|
27
21
|
export declare function getPgConnectionOptions(config: PgConfig, profileName: string): ClientConfig;
|
|
28
22
|
export {};
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get PostgreSQL connection options for a specific profile
|
|
3
|
-
*
|
|
4
|
-
* @param config - Configuration object
|
|
5
|
-
* @param profileName - Profile name
|
|
6
|
-
* @returns pg ClientConfig options
|
|
7
|
-
*/
|
|
8
1
|
export function getPgConnectionOptions(config, profileName) {
|
|
9
2
|
const profile = config.profiles[profileName];
|
|
10
3
|
if (!profile) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { OutputFormat } from './database.js';
|
|
2
|
+
export type PgRow = Record<string, unknown>;
|
|
3
|
+
export type PgField = {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
type Formatter = (rows: PgRow[], fields: PgField[]) => string;
|
|
7
|
+
export declare const FORMATTERS: Record<OutputFormat, Formatter>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { encode } from '@toon-format/toon';
|
|
2
|
+
function formatAsCsv(rows, fields) {
|
|
3
|
+
if (!rows || rows.length === 0) {
|
|
4
|
+
return '';
|
|
5
|
+
}
|
|
6
|
+
const columnNames = fields.map((f) => f.name);
|
|
7
|
+
let csv = columnNames.join(',') + '\n';
|
|
8
|
+
for (const row of rows) {
|
|
9
|
+
const values = columnNames.map((name) => {
|
|
10
|
+
const value = row[name] ?? '';
|
|
11
|
+
const str = String(value);
|
|
12
|
+
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
|
13
|
+
return '"' + str.replaceAll('"', '""') + '"';
|
|
14
|
+
}
|
|
15
|
+
return str;
|
|
16
|
+
});
|
|
17
|
+
csv += values.join(',') + '\n';
|
|
18
|
+
}
|
|
19
|
+
return csv;
|
|
20
|
+
}
|
|
21
|
+
function formatAsJson(rows) {
|
|
22
|
+
return JSON.stringify(rows, null, 2);
|
|
23
|
+
}
|
|
24
|
+
function formatAsTable(rows, fields) {
|
|
25
|
+
if (!rows || rows.length === 0) {
|
|
26
|
+
return 'No results';
|
|
27
|
+
}
|
|
28
|
+
const columnNames = fields.map((f) => f.name);
|
|
29
|
+
const columnWidths = columnNames.map((name) => {
|
|
30
|
+
const dataWidth = Math.max(...rows.map((row) => String(row[name] ?? '').length));
|
|
31
|
+
return Math.max(name.length, dataWidth, 3);
|
|
32
|
+
});
|
|
33
|
+
let table = '┌' + columnWidths.map((w) => '─'.repeat(w + 2)).join('┬') + '┐\n';
|
|
34
|
+
table += '│ ' + columnNames.map((name, i) => name.padEnd(columnWidths[i])).join(' │ ') + ' │\n';
|
|
35
|
+
table += '├' + columnWidths.map((w) => '─'.repeat(w + 2)).join('┼') + '┤\n';
|
|
36
|
+
for (const row of rows) {
|
|
37
|
+
table +=
|
|
38
|
+
'│ ' +
|
|
39
|
+
columnNames
|
|
40
|
+
.map((name, i) => {
|
|
41
|
+
const value = row[name] ?? 'NULL';
|
|
42
|
+
return String(value).padEnd(columnWidths[i]);
|
|
43
|
+
})
|
|
44
|
+
.join(' │ ') +
|
|
45
|
+
' │\n';
|
|
46
|
+
}
|
|
47
|
+
table += '└' + columnWidths.map((w) => '─'.repeat(w + 2)).join('┴') + '┘';
|
|
48
|
+
return table;
|
|
49
|
+
}
|
|
50
|
+
function formatAsToon(rows) {
|
|
51
|
+
if (!rows || rows.length === 0) {
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
const serializedRows = rows.map((row) => {
|
|
55
|
+
const serialized = {};
|
|
56
|
+
for (const [key, value] of Object.entries(row)) {
|
|
57
|
+
if (value instanceof Date) {
|
|
58
|
+
serialized[key] = Number.isNaN(value.getTime()) ? null : value.toISOString();
|
|
59
|
+
}
|
|
60
|
+
else if (Buffer.isBuffer(value)) {
|
|
61
|
+
serialized[key] = value.toString('base64');
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
serialized[key] = value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return serialized;
|
|
68
|
+
});
|
|
69
|
+
return encode(serializedRows);
|
|
70
|
+
}
|
|
71
|
+
export const FORMATTERS = {
|
|
72
|
+
csv: formatAsCsv,
|
|
73
|
+
json: formatAsJson,
|
|
74
|
+
table: formatAsTable,
|
|
75
|
+
toon: formatAsToon,
|
|
76
|
+
};
|
package/dist/psql/index.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export type { PgJsonConfig } from '../config.js';
|
|
2
1
|
export type { ConnectionTestResult } from './database.js';
|
|
3
|
-
export { closeConnections, describeTable, executeQuery, explainQuery,
|
|
2
|
+
export { closeConnections, describeTable, executeQuery, explainQuery, listDatabases, listTables, showIndexes, testDirectConnection, } from './postgres-client.js';
|
package/dist/psql/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { closeConnections, describeTable, executeQuery, explainQuery,
|
|
1
|
+
export { closeConnections, describeTable, executeQuery, explainQuery, listDatabases, listTables, showIndexes, testDirectConnection, } from './postgres-client.js';
|
|
@@ -1,60 +1,11 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { Config } from '@oclif/core';
|
|
2
|
+
import type { DatabaseProfile } from './config-loader.js';
|
|
3
3
|
import type { ConnectionTestResult, DatabaseListResult, ExplainResult, IndexResult, OutputFormat, QueryResult, TableListResult, TableStructureResult } from './database.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare function
|
|
9
|
-
|
|
10
|
-
* Get the loaded PostgreSQL config, initializing if needed
|
|
11
|
-
*/
|
|
12
|
-
export declare function getPgConfig(): Promise<PgConfig>;
|
|
13
|
-
/**
|
|
14
|
-
* Execute SQL query
|
|
15
|
-
* @param query - SQL query to execute
|
|
16
|
-
* @param profile - Database profile name
|
|
17
|
-
* @param format - Output format
|
|
18
|
-
* @param skipConfirmation - Skip confirmation for destructive operations
|
|
19
|
-
*/
|
|
20
|
-
export declare function executeQuery(query: string, profile: string, format?: OutputFormat, skipConfirmation?: boolean): Promise<QueryResult>;
|
|
21
|
-
/**
|
|
22
|
-
* List all databases
|
|
23
|
-
* @param profile - Database profile name
|
|
24
|
-
*/
|
|
25
|
-
export declare function listDatabases(profile: string): Promise<DatabaseListResult>;
|
|
26
|
-
/**
|
|
27
|
-
* List all tables in current database
|
|
28
|
-
* @param profile - Database profile name
|
|
29
|
-
*/
|
|
30
|
-
export declare function listTables(profile: string): Promise<TableListResult>;
|
|
31
|
-
/**
|
|
32
|
-
* Describe table structure
|
|
33
|
-
* @param profile - Database profile name
|
|
34
|
-
* @param table - Table name
|
|
35
|
-
* @param format - Output format
|
|
36
|
-
*/
|
|
37
|
-
export declare function describeTable(profile: string, table: string, format?: 'json' | 'table' | 'toon'): Promise<TableStructureResult>;
|
|
38
|
-
/**
|
|
39
|
-
* Show table indexes
|
|
40
|
-
* @param profile - Database profile name
|
|
41
|
-
* @param table - Table name
|
|
42
|
-
* @param format - Output format
|
|
43
|
-
*/
|
|
44
|
-
export declare function showIndexes(profile: string, table: string, format?: 'json' | 'table' | 'toon'): Promise<IndexResult>;
|
|
45
|
-
/**
|
|
46
|
-
* Explain query execution plan
|
|
47
|
-
* @param profile - Database profile name
|
|
48
|
-
* @param query - SQL query to explain
|
|
49
|
-
* @param format - Output format
|
|
50
|
-
*/
|
|
51
|
-
export declare function explainQuery(profile: string, query: string, format?: 'json' | 'table' | 'toon'): Promise<ExplainResult>;
|
|
52
|
-
/**
|
|
53
|
-
* Test a connection directly with profile options (without loading JSON config)
|
|
54
|
-
* @param profile - Database connection profile options
|
|
55
|
-
*/
|
|
4
|
+
export declare function executeQuery(config: Config, query: string, profile?: string, format?: OutputFormat, skipConfirmation?: boolean): Promise<QueryResult>;
|
|
5
|
+
export declare function listDatabases(config: Config, profile?: string): Promise<DatabaseListResult>;
|
|
6
|
+
export declare function listTables(config: Config, profile?: string): Promise<TableListResult>;
|
|
7
|
+
export declare function describeTable(config: Config, table: string, profile?: string, format?: 'json' | 'table' | 'toon'): Promise<TableStructureResult>;
|
|
8
|
+
export declare function showIndexes(config: Config, table: string, profile?: string, format?: 'json' | 'table' | 'toon'): Promise<IndexResult>;
|
|
9
|
+
export declare function explainQuery(config: Config, query: string, profile?: string, format?: 'json' | 'table' | 'toon'): Promise<ExplainResult>;
|
|
56
10
|
export declare function testDirectConnection(profile: DatabaseProfile): Promise<ConnectionTestResult>;
|
|
57
|
-
/**
|
|
58
|
-
* Close all connections
|
|
59
|
-
*/
|
|
60
11
|
export declare function closeConnections(): Promise<void>;
|
|
@@ -1,127 +1,70 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createProfileManager } from '@hesed/plugin-lib';
|
|
2
2
|
import { PostgreSQLUtil } from './postgres-utils.js';
|
|
3
3
|
let pgUtil = null;
|
|
4
4
|
let cachedConfig = null;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
cachedConfigDir = dir;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Initialize (or return cached) PostgreSQLUtil
|
|
15
|
-
*/
|
|
16
|
-
async function initPg() {
|
|
5
|
+
const DEFAULT_SAFETY_CONFIG = {
|
|
6
|
+
blacklistedOperations: ['DROP DATABASE'],
|
|
7
|
+
defaultLimit: 100,
|
|
8
|
+
requireConfirmationFor: ['DELETE', 'UPDATE', 'DROP', 'TRUNCATE', 'ALTER'],
|
|
9
|
+
};
|
|
10
|
+
async function initPg(config) {
|
|
17
11
|
if (pgUtil)
|
|
18
12
|
return pgUtil;
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
const pm = createProfileManager(config);
|
|
14
|
+
const profiles = await pm.readProfiles();
|
|
15
|
+
if (!profiles) {
|
|
16
|
+
throw new Error('No profile found.');
|
|
21
17
|
}
|
|
22
|
-
const
|
|
23
|
-
if (!
|
|
24
|
-
throw new Error('Missing
|
|
18
|
+
const defaultProfile = await pm.getDefaultProfile();
|
|
19
|
+
if (!defaultProfile) {
|
|
20
|
+
throw new Error('Missing default profile.');
|
|
25
21
|
}
|
|
26
22
|
cachedConfig = {
|
|
27
23
|
defaultFormat: 'table',
|
|
28
|
-
defaultProfile
|
|
29
|
-
profiles
|
|
30
|
-
safety:
|
|
31
|
-
blacklistedOperations: ['DROP DATABASE'],
|
|
32
|
-
defaultLimit: 100,
|
|
33
|
-
requireConfirmationFor: ['DELETE', 'UPDATE', 'DROP', 'TRUNCATE', 'ALTER'],
|
|
34
|
-
},
|
|
24
|
+
defaultProfile,
|
|
25
|
+
profiles,
|
|
26
|
+
safety: DEFAULT_SAFETY_CONFIG,
|
|
35
27
|
};
|
|
36
28
|
pgUtil = new PostgreSQLUtil(cachedConfig);
|
|
37
29
|
return pgUtil;
|
|
38
30
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (!cachedConfig) {
|
|
44
|
-
await initPg();
|
|
45
|
-
}
|
|
46
|
-
return cachedConfig;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Execute SQL query
|
|
50
|
-
* @param query - SQL query to execute
|
|
51
|
-
* @param profile - Database profile name
|
|
52
|
-
* @param format - Output format
|
|
53
|
-
* @param skipConfirmation - Skip confirmation for destructive operations
|
|
54
|
-
*/
|
|
55
|
-
export async function executeQuery(query, profile, format = 'table', skipConfirmation = false) {
|
|
56
|
-
return (await initPg()).executeQuery(profile, query, format, skipConfirmation);
|
|
31
|
+
// eslint-disable-next-line max-params
|
|
32
|
+
export async function executeQuery(config, query, profile, format = 'table', skipConfirmation = false) {
|
|
33
|
+
const profileName = profile ?? cachedConfig?.defaultProfile ?? 'default';
|
|
34
|
+
return (await initPg(config)).executeQuery(profileName, query, format, skipConfirmation);
|
|
57
35
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*/
|
|
62
|
-
export async function listDatabases(profile) {
|
|
63
|
-
return (await initPg()).listDatabases(profile);
|
|
36
|
+
export async function listDatabases(config, profile) {
|
|
37
|
+
const profileName = profile ?? cachedConfig?.defaultProfile ?? 'default';
|
|
38
|
+
return (await initPg(config)).listDatabases(profileName);
|
|
64
39
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*/
|
|
69
|
-
export async function listTables(profile) {
|
|
70
|
-
return (await initPg()).listTables(profile);
|
|
40
|
+
export async function listTables(config, profile) {
|
|
41
|
+
const profileName = profile ?? cachedConfig?.defaultProfile ?? 'default';
|
|
42
|
+
return (await initPg(config)).listTables(profileName);
|
|
71
43
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
* @param table - Table name
|
|
76
|
-
* @param format - Output format
|
|
77
|
-
*/
|
|
78
|
-
export async function describeTable(profile, table, format = 'table') {
|
|
79
|
-
return (await initPg()).describeTable(profile, table, format);
|
|
44
|
+
export async function describeTable(config, table, profile, format = 'table') {
|
|
45
|
+
const profileName = profile ?? cachedConfig?.defaultProfile ?? 'default';
|
|
46
|
+
return (await initPg(config)).describeTable(profileName, table, format);
|
|
80
47
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
* @param table - Table name
|
|
85
|
-
* @param format - Output format
|
|
86
|
-
*/
|
|
87
|
-
export async function showIndexes(profile, table, format = 'table') {
|
|
88
|
-
return (await initPg()).showIndexes(profile, table, format);
|
|
48
|
+
export async function showIndexes(config, table, profile, format = 'table') {
|
|
49
|
+
const profileName = profile ?? cachedConfig?.defaultProfile ?? 'default';
|
|
50
|
+
return (await initPg(config)).showIndexes(profileName, table, format);
|
|
89
51
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
* @param query - SQL query to explain
|
|
94
|
-
* @param format - Output format
|
|
95
|
-
*/
|
|
96
|
-
export async function explainQuery(profile, query, format = 'table') {
|
|
97
|
-
return (await initPg()).explainQuery(profile, query, format);
|
|
52
|
+
export async function explainQuery(config, query, profile, format = 'table') {
|
|
53
|
+
const profileName = profile ?? cachedConfig?.defaultProfile ?? 'default';
|
|
54
|
+
return (await initPg(config)).explainQuery(profileName, query, format);
|
|
98
55
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Test a connection directly with profile options (without loading JSON config)
|
|
101
|
-
* @param profile - Database connection profile options
|
|
102
|
-
*/
|
|
103
56
|
export async function testDirectConnection(profile) {
|
|
104
|
-
const
|
|
57
|
+
const testConfig = {
|
|
105
58
|
defaultFormat: 'table',
|
|
106
|
-
defaultProfile: '
|
|
107
|
-
profiles: {
|
|
108
|
-
safety:
|
|
109
|
-
blacklistedOperations: [],
|
|
110
|
-
defaultLimit: 100,
|
|
111
|
-
requireConfirmationFor: [],
|
|
112
|
-
},
|
|
59
|
+
defaultProfile: 'default',
|
|
60
|
+
profiles: { default: profile },
|
|
61
|
+
safety: DEFAULT_SAFETY_CONFIG,
|
|
113
62
|
};
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
finally {
|
|
119
|
-
await tempUtil.closeAll();
|
|
120
|
-
}
|
|
63
|
+
const util = new PostgreSQLUtil(testConfig);
|
|
64
|
+
const result = await util.testConnection('default');
|
|
65
|
+
await util.closeAll();
|
|
66
|
+
return result;
|
|
121
67
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Close all connections
|
|
124
|
-
*/
|
|
125
68
|
export async function closeConnections() {
|
|
126
69
|
if (pgUtil) {
|
|
127
70
|
await pgUtil.closeAll();
|
|
@@ -1,72 +1,18 @@
|
|
|
1
1
|
import type { PgConfig } from './config-loader.js';
|
|
2
2
|
import type { ConnectionTestResult, DatabaseListResult, DatabaseUtil, ExplainResult, IndexResult, OutputFormat, QueryResult, TableListResult, TableStructureResult } from './database.js';
|
|
3
|
-
type PgRow = Record<string, unknown>;
|
|
4
|
-
type PgField = {
|
|
5
|
-
name: string;
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* PostgreSQL Database Utility
|
|
9
|
-
* Provides core database operations with safety validation and formatting
|
|
10
|
-
*/
|
|
11
3
|
export declare class PostgreSQLUtil implements DatabaseUtil {
|
|
12
4
|
private config;
|
|
13
5
|
private connections;
|
|
14
6
|
constructor(config: PgConfig);
|
|
15
|
-
/**
|
|
16
|
-
* Close all connections
|
|
17
|
-
*/
|
|
18
7
|
closeAll(): Promise<void>;
|
|
19
|
-
/**
|
|
20
|
-
* Describe table structure
|
|
21
|
-
*/
|
|
22
8
|
describeTable(profileName: string, table: string, format?: 'json' | 'table' | 'toon'): Promise<TableStructureResult>;
|
|
23
|
-
/**
|
|
24
|
-
* Validate and execute a SQL query
|
|
25
|
-
*/
|
|
26
9
|
executeQuery(profileName: string, query: string, format?: OutputFormat, skipConfirmation?: boolean): Promise<QueryResult>;
|
|
27
|
-
/**
|
|
28
|
-
* Explain query execution plan
|
|
29
|
-
*/
|
|
30
10
|
explainQuery(profileName: string, query: string, format?: 'json' | 'table' | 'toon'): Promise<ExplainResult>;
|
|
31
|
-
/**
|
|
32
|
-
* Format query results as CSV
|
|
33
|
-
*/
|
|
34
|
-
formatAsCsv(rows: PgRow[], fields: PgField[]): string;
|
|
35
|
-
/**
|
|
36
|
-
* Format query results as JSON
|
|
37
|
-
*/
|
|
38
|
-
formatAsJson(rows: PgRow[]): string;
|
|
39
|
-
/**
|
|
40
|
-
* Format query results as table
|
|
41
|
-
*/
|
|
42
|
-
formatAsTable(rows: PgRow[], fields: PgField[]): string;
|
|
43
|
-
/**
|
|
44
|
-
* Format query results as TOON
|
|
45
|
-
*/
|
|
46
|
-
formatAsToon(rows: PgRow[]): string;
|
|
47
|
-
/**
|
|
48
|
-
* List all databases
|
|
49
|
-
*/
|
|
50
11
|
listDatabases(profileName: string): Promise<DatabaseListResult>;
|
|
51
|
-
/**
|
|
52
|
-
* List all tables in current database
|
|
53
|
-
*/
|
|
54
12
|
listTables(profileName: string): Promise<TableListResult>;
|
|
55
|
-
/**
|
|
56
|
-
* Show table indexes
|
|
57
|
-
*/
|
|
58
13
|
showIndexes(profileName: string, table: string, format?: 'json' | 'table' | 'toon'): Promise<IndexResult>;
|
|
59
|
-
/**
|
|
60
|
-
* Test database connection
|
|
61
|
-
*/
|
|
62
14
|
testConnection(profileName: string): Promise<ConnectionTestResult>;
|
|
63
|
-
|
|
64
|
-
* Format rows for SELECT/EXPLAIN query result
|
|
65
|
-
*/
|
|
15
|
+
private formatRows;
|
|
66
16
|
private formatSelectResult;
|
|
67
|
-
/**
|
|
68
|
-
* Get or create PostgreSQL client for a profile
|
|
69
|
-
*/
|
|
70
17
|
private getConnection;
|
|
71
18
|
}
|
|
72
|
-
export {};
|