@slates-integrations/postgresql 0.2.0-rc.6 → 0.2.0-rc.9

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.
@@ -1,231 +0,0 @@
1
- import { SlateTool } from '@slates/provider';
2
- import { spec } from '../spec';
3
- import { createClient, escapeIdentifier } from '../lib/helpers';
4
- import { z } from 'zod';
5
-
6
- export let describeTable = SlateTool.create(spec, {
7
- name: 'Describe Table',
8
- key: 'describe_table',
9
- description: `Get detailed schema information for a specific table, including columns, data types, constraints, indexes, and foreign keys.
10
- Useful for understanding table structure before building queries or modifying schemas.`,
11
- tags: {
12
- readOnly: true
13
- }
14
- })
15
- .input(
16
- z.object({
17
- tableName: z.string().describe('Name of the table to describe'),
18
- schemaName: z
19
- .string()
20
- .optional()
21
- .describe('Schema containing the table. Defaults to the configured default schema.')
22
- })
23
- )
24
- .output(
25
- z.object({
26
- tableName: z.string().describe('Name of the described table'),
27
- schemaName: z.string().describe('Schema of the described table'),
28
- columns: z
29
- .array(
30
- z.object({
31
- columnName: z.string().describe('Column name'),
32
- dataType: z.string().describe('PostgreSQL data type'),
33
- isNullable: z.boolean().describe('Whether the column allows NULL values'),
34
- defaultValue: z.string().nullable().describe('Default value expression'),
35
- maxLength: z
36
- .number()
37
- .nullable()
38
- .describe('Maximum character length for character types'),
39
- isPrimaryKey: z
40
- .boolean()
41
- .describe('Whether this column is part of the primary key'),
42
- ordinalPosition: z.number().describe('Column position in the table')
43
- })
44
- )
45
- .describe('Column definitions'),
46
- indexes: z
47
- .array(
48
- z.object({
49
- indexName: z.string().describe('Name of the index'),
50
- isUnique: z.boolean().describe('Whether this is a unique index'),
51
- isPrimary: z.boolean().describe('Whether this is the primary key index'),
52
- indexColumns: z.array(z.string()).describe('Columns included in the index'),
53
- indexType: z
54
- .string()
55
- .describe('Index access method (btree, hash, gin, gist, etc.)')
56
- })
57
- )
58
- .describe('Indexes on the table'),
59
- foreignKeys: z
60
- .array(
61
- z.object({
62
- constraintName: z.string().describe('Name of the foreign key constraint'),
63
- columnName: z.string().describe('Local column name'),
64
- referencedTable: z.string().describe('Referenced table (schema.table)'),
65
- referencedColumn: z.string().describe('Referenced column name')
66
- })
67
- )
68
- .describe('Foreign key relationships'),
69
- estimatedRowCount: z.number().nullable().describe('Estimated number of rows'),
70
- sizeFormatted: z.string().nullable().describe('Human-readable table size')
71
- })
72
- )
73
- .handleInvocation(async ctx => {
74
- let client = createClient(ctx.auth, ctx.config);
75
- let schema = ctx.input.schemaName || ctx.config.defaultSchema;
76
- let table = ctx.input.tableName;
77
-
78
- ctx.info(`Describing table: ${schema}.${table}`);
79
-
80
- // Fetch columns
81
- let columnsResult = await client.query(
82
- `
83
- SELECT
84
- c.column_name,
85
- c.data_type,
86
- c.udt_name,
87
- c.is_nullable,
88
- c.column_default,
89
- c.character_maximum_length,
90
- c.ordinal_position,
91
- COALESCE(
92
- (SELECT true FROM information_schema.key_column_usage kcu
93
- JOIN information_schema.table_constraints tc
94
- ON tc.constraint_name = kcu.constraint_name
95
- AND tc.table_schema = kcu.table_schema
96
- WHERE tc.constraint_type = 'PRIMARY KEY'
97
- AND kcu.table_schema = c.table_schema
98
- AND kcu.table_name = c.table_name
99
- AND kcu.column_name = c.column_name
100
- LIMIT 1),
101
- false
102
- ) AS is_primary_key
103
- FROM information_schema.columns c
104
- WHERE c.table_schema = '${schema.replace(/'/g, "''")}'
105
- AND c.table_name = '${table.replace(/'/g, "''")}'
106
- ORDER BY c.ordinal_position
107
- `,
108
- ctx.config.queryTimeout
109
- );
110
-
111
- // Fetch indexes
112
- let indexesResult = await client.query(
113
- `
114
- SELECT
115
- i.relname AS index_name,
116
- ix.indisunique AS is_unique,
117
- ix.indisprimary AS is_primary,
118
- am.amname AS index_type,
119
- array_agg(a.attname ORDER BY array_position(ix.indkey, a.attnum)) AS index_columns
120
- FROM pg_index ix
121
- JOIN pg_class t ON t.oid = ix.indrelid
122
- JOIN pg_class i ON i.oid = ix.indexrelid
123
- JOIN pg_namespace n ON n.oid = t.relnamespace
124
- JOIN pg_am am ON am.oid = i.relam
125
- JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
126
- WHERE t.relname = '${table.replace(/'/g, "''")}'
127
- AND n.nspname = '${schema.replace(/'/g, "''")}'
128
- GROUP BY i.relname, ix.indisunique, ix.indisprimary, am.amname
129
- ORDER BY i.relname
130
- `,
131
- ctx.config.queryTimeout
132
- );
133
-
134
- // Fetch foreign keys
135
- let fkResult = await client.query(
136
- `
137
- SELECT
138
- tc.constraint_name,
139
- kcu.column_name,
140
- ccu.table_schema || '.' || ccu.table_name AS referenced_table,
141
- ccu.column_name AS referenced_column
142
- FROM information_schema.table_constraints tc
143
- JOIN information_schema.key_column_usage kcu
144
- ON tc.constraint_name = kcu.constraint_name
145
- AND tc.table_schema = kcu.table_schema
146
- JOIN information_schema.constraint_column_usage ccu
147
- ON ccu.constraint_name = tc.constraint_name
148
- AND ccu.table_schema = tc.table_schema
149
- WHERE tc.constraint_type = 'FOREIGN KEY'
150
- AND tc.table_schema = '${schema.replace(/'/g, "''")}'
151
- AND tc.table_name = '${table.replace(/'/g, "''")}'
152
- ORDER BY tc.constraint_name
153
- `,
154
- ctx.config.queryTimeout
155
- );
156
-
157
- // Fetch size info
158
- let sizeResult = await client.query(
159
- `
160
- SELECT
161
- c.reltuples::bigint AS estimated_row_count,
162
- pg_size_pretty(pg_total_relation_size(c.oid)) AS size_formatted
163
- FROM pg_class c
164
- JOIN pg_namespace n ON n.oid = c.relnamespace
165
- WHERE c.relname = '${table.replace(/'/g, "''")}'
166
- AND n.nspname = '${schema.replace(/'/g, "''")}'
167
- LIMIT 1
168
- `,
169
- ctx.config.queryTimeout
170
- );
171
-
172
- let columns = columnsResult.rows.map((row: any) => ({
173
- columnName: row.column_name as string,
174
- dataType:
175
- row.udt_name === 'int4'
176
- ? 'integer'
177
- : row.udt_name === 'int8'
178
- ? 'bigint'
179
- : row.udt_name === 'int2'
180
- ? 'smallint'
181
- : row.udt_name === 'float4'
182
- ? 'real'
183
- : row.udt_name === 'float8'
184
- ? 'double precision'
185
- : row.udt_name === 'bool'
186
- ? 'boolean'
187
- : (row.data_type as string),
188
- isNullable: row.is_nullable === 'YES',
189
- defaultValue: row.column_default as string | null,
190
- maxLength:
191
- row.character_maximum_length != null ? Number(row.character_maximum_length) : null,
192
- isPrimaryKey: row.is_primary_key === true,
193
- ordinalPosition: Number(row.ordinal_position)
194
- }));
195
-
196
- let indexes = indexesResult.rows.map((row: any) => ({
197
- indexName: row.index_name as string,
198
- isUnique: row.is_unique === true,
199
- isPrimary: row.is_primary === true,
200
- indexColumns: Array.isArray(row.index_columns)
201
- ? row.index_columns
202
- : typeof row.index_columns === 'string'
203
- ? row.index_columns.replace(/[{}]/g, '').split(',')
204
- : [],
205
- indexType: row.index_type as string
206
- }));
207
-
208
- let foreignKeys = fkResult.rows.map((row: any) => ({
209
- constraintName: row.constraint_name as string,
210
- columnName: row.column_name as string,
211
- referencedTable: row.referenced_table as string,
212
- referencedColumn: row.referenced_column as string
213
- }));
214
-
215
- let sizeRow = sizeResult.rows[0] as any;
216
-
217
- return {
218
- output: {
219
- tableName: table,
220
- schemaName: schema,
221
- columns,
222
- indexes,
223
- foreignKeys,
224
- estimatedRowCount:
225
- sizeRow?.estimated_row_count != null ? Number(sizeRow.estimated_row_count) : null,
226
- sizeFormatted: (sizeRow?.size_formatted as string) ?? null
227
- },
228
- message: `Table \`${schema}.${table}\` has **${columns.length}** column(s), **${indexes.length}** index(es), and **${foreignKeys.length}** foreign key(s).`
229
- };
230
- })
231
- .build();
@@ -1,95 +0,0 @@
1
- import { SlateTool } from '@slates/provider';
2
- import { spec } from '../spec';
3
- import { createClient } from '../lib/helpers';
4
- import { z } from 'zod';
5
-
6
- export let executeQuery = SlateTool.create(spec, {
7
- name: 'Execute SQL Query',
8
- key: 'execute_query',
9
- description: `Execute an arbitrary SQL query against the PostgreSQL database. Supports all SQL operations including SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, and more.
10
- Returns column metadata and result rows for SELECT queries, or affected row counts for DML statements.
11
- Supports complex queries with joins, subqueries, CTEs, window functions, and aggregations.`,
12
- instructions: [
13
- 'Always validate SQL syntax before executing to avoid errors.',
14
- 'Use parameterized-style quoting for user-provided values to prevent SQL injection.',
15
- 'For large result sets, use LIMIT to control the number of returned rows.'
16
- ],
17
- constraints: [
18
- 'Query execution is subject to the configured timeout.',
19
- 'Results are limited by the configured maxRows setting when no explicit LIMIT is provided.'
20
- ],
21
- tags: {
22
- destructive: false,
23
- readOnly: false
24
- }
25
- })
26
- .input(
27
- z.object({
28
- sql: z.string().describe('The SQL query to execute'),
29
- maxRows: z
30
- .number()
31
- .optional()
32
- .describe(
33
- 'Maximum number of rows to return. Overrides the default maxRows config. Only applicable for SELECT queries.'
34
- )
35
- })
36
- )
37
- .output(
38
- z.object({
39
- columns: z
40
- .array(
41
- z.object({
42
- name: z.string().describe('Column name'),
43
- type: z.string().describe('PostgreSQL data type')
44
- })
45
- )
46
- .describe('Column metadata for the result set'),
47
- rows: z
48
- .array(z.record(z.string(), z.any()))
49
- .describe('Result rows as key-value objects'),
50
- rowCount: z
51
- .number()
52
- .nullable()
53
- .describe('Number of rows affected (for DML) or returned'),
54
- command: z
55
- .string()
56
- .describe('The SQL command that was executed (e.g., SELECT, INSERT, UPDATE)')
57
- })
58
- )
59
- .handleInvocation(async ctx => {
60
- let client = createClient(ctx.auth, ctx.config);
61
- let sql = ctx.input.sql.trim();
62
- let maxRows = ctx.input.maxRows ?? ctx.config.maxRows;
63
-
64
- // If it's a SELECT query without LIMIT, add one
65
- let isSelect = /^\s*(SELECT|WITH)\b/i.test(sql);
66
- let hasLimit = /\bLIMIT\s+\d+/i.test(sql);
67
-
68
- let querySql = sql;
69
- if (isSelect && !hasLimit && maxRows > 0) {
70
- // Remove trailing semicolon if present before adding LIMIT
71
- querySql = querySql.replace(/;\s*$/, '');
72
- querySql = `${querySql} LIMIT ${maxRows}`;
73
- }
74
-
75
- ctx.info(
76
- `Executing SQL query: ${querySql.substring(0, 200)}${querySql.length > 200 ? '...' : ''}`
77
- );
78
-
79
- let result = await client.query(querySql, ctx.config.queryTimeout);
80
-
81
- let displayColumns = result.columns.map(c => ({ name: c.name, type: c.type }));
82
-
83
- return {
84
- output: {
85
- columns: displayColumns,
86
- rows: result.rows,
87
- rowCount: result.rowCount,
88
- command: result.command
89
- },
90
- message: isSelect
91
- ? `Query returned **${result.rows.length}** row(s) with **${result.columns.length}** column(s).`
92
- : `\`${result.command}\` completed. **${result.rowCount ?? 0}** row(s) affected.`
93
- };
94
- })
95
- .build();
@@ -1,12 +0,0 @@
1
- export { executeQuery } from './execute-query';
2
- export { listTables } from './list-tables';
3
- export { describeTable } from './describe-table';
4
- export { insertRows } from './insert-rows';
5
- export { updateRows } from './update-rows';
6
- export { deleteRows } from './delete-rows';
7
- export { manageTable } from './manage-table';
8
- export { manageIndexes } from './manage-indexes';
9
- export { listSchemas } from './list-schemas';
10
- export { manageRoles } from './manage-roles';
11
- export { manageSchemas } from './manage-schemas';
12
- export { manageViews } from './manage-views';
@@ -1,133 +0,0 @@
1
- import { SlateTool } from '@slates/provider';
2
- import { spec } from '../spec';
3
- import {
4
- createClient,
5
- escapeIdentifier,
6
- escapeLiteral,
7
- qualifiedTableName
8
- } from '../lib/helpers';
9
- import { postgresServiceError } from '../lib/errors';
10
- import { z } from 'zod';
11
-
12
- export let insertRows = SlateTool.create(spec, {
13
- name: 'Insert Rows',
14
- key: 'insert_rows',
15
- description: `Insert one or more rows into a PostgreSQL table. Provide the data as an array of objects where keys are column names and values are the data to insert.
16
- Supports inserting multiple rows in a single operation and can optionally return the inserted rows.`,
17
- instructions: [
18
- 'Column names in the row objects must exactly match the table column names.',
19
- 'Values are automatically escaped to prevent SQL injection.',
20
- 'Use null for NULL values.'
21
- ],
22
- tags: {
23
- destructive: false
24
- }
25
- })
26
- .input(
27
- z.object({
28
- tableName: z.string().describe('Name of the table to insert into'),
29
- schemaName: z.string().optional().describe('Schema containing the table'),
30
- rows: z
31
- .array(z.record(z.string(), z.any()))
32
- .min(1)
33
- .describe('Array of row objects to insert, where keys are column names'),
34
- returning: z
35
- .boolean()
36
- .optional()
37
- .default(true)
38
- .describe('Whether to return the inserted rows using RETURNING *'),
39
- onConflict: z
40
- .enum(['error', 'ignore', 'update'])
41
- .optional()
42
- .default('error')
43
- .describe(
44
- 'Behavior on unique constraint conflict: error (default), ignore (DO NOTHING), or update (DO UPDATE SET)'
45
- ),
46
- conflictColumns: z
47
- .array(z.string())
48
- .optional()
49
- .describe(
50
- 'Columns that define the conflict target (required when onConflict is "ignore" or "update")'
51
- )
52
- })
53
- )
54
- .output(
55
- z.object({
56
- insertedCount: z.number().describe('Number of rows inserted'),
57
- returnedRows: z
58
- .array(z.record(z.string(), z.any()))
59
- .describe('Inserted rows (if returning was enabled)')
60
- })
61
- )
62
- .handleInvocation(async ctx => {
63
- let client = createClient(ctx.auth, ctx.config);
64
- let schema = ctx.input.schemaName || ctx.config.defaultSchema;
65
- let fullTableName = qualifiedTableName(ctx.input.tableName, schema);
66
-
67
- // Collect all unique column names from all rows
68
- let columnSet = new Set<string>();
69
- for (let row of ctx.input.rows) {
70
- for (let key of Object.keys(row)) {
71
- columnSet.add(key);
72
- }
73
- }
74
- let columns = Array.from(columnSet);
75
-
76
- // Build values list
77
- let valueClauses: string[] = [];
78
- for (let row of ctx.input.rows) {
79
- let values = columns.map(col => {
80
- let val = row[col];
81
- if (val === null || val === undefined) return 'NULL';
82
- if (typeof val === 'number') return String(val);
83
- if (typeof val === 'boolean') return val ? 'TRUE' : 'FALSE';
84
- if (typeof val === 'object') return escapeLiteral(JSON.stringify(val));
85
- return escapeLiteral(String(val));
86
- });
87
- valueClauses.push(`(${values.join(', ')})`);
88
- }
89
-
90
- let columnList = columns.map(escapeIdentifier).join(', ');
91
- let sql = `INSERT INTO ${fullTableName} (${columnList}) VALUES ${valueClauses.join(', ')}`;
92
-
93
- // Handle conflict
94
- if (ctx.input.onConflict === 'ignore') {
95
- let conflictTarget = ctx.input.conflictColumns?.length
96
- ? `(${ctx.input.conflictColumns.map(escapeIdentifier).join(', ')})`
97
- : '';
98
- sql += ` ON CONFLICT ${conflictTarget} DO NOTHING`;
99
- } else if (ctx.input.onConflict === 'update') {
100
- let conflictCols = ctx.input.conflictColumns;
101
- if (!conflictCols?.length) {
102
- throw postgresServiceError(
103
- 'conflictColumns must be specified when onConflict is "update"'
104
- );
105
- }
106
- let conflictTarget = `(${conflictCols.map(escapeIdentifier).join(', ')})`;
107
- let updateCols = columns.filter(c => !conflictCols.includes(c));
108
- let setClauses = updateCols.map(
109
- c => `${escapeIdentifier(c)} = EXCLUDED.${escapeIdentifier(c)}`
110
- );
111
- if (setClauses.length > 0) {
112
- sql += ` ON CONFLICT ${conflictTarget} DO UPDATE SET ${setClauses.join(', ')}`;
113
- } else {
114
- sql += ` ON CONFLICT ${conflictTarget} DO NOTHING`;
115
- }
116
- }
117
-
118
- if (ctx.input.returning) {
119
- sql += ' RETURNING *';
120
- }
121
-
122
- ctx.info(`Inserting ${ctx.input.rows.length} row(s) into ${fullTableName}`);
123
- let result = await client.query(sql, ctx.config.queryTimeout);
124
-
125
- return {
126
- output: {
127
- insertedCount: result.rowCount ?? ctx.input.rows.length,
128
- returnedRows: result.rows
129
- },
130
- message: `Inserted **${result.rowCount ?? ctx.input.rows.length}** row(s) into \`${ctx.input.tableName}\`.`
131
- };
132
- })
133
- .build();
@@ -1,80 +0,0 @@
1
- import { SlateTool } from '@slates/provider';
2
- import { spec } from '../spec';
3
- import { createClient } from '../lib/helpers';
4
- import { z } from 'zod';
5
-
6
- export let listSchemas = SlateTool.create(spec, {
7
- name: 'List Schemas',
8
- key: 'list_schemas',
9
- description: `List all schemas in the PostgreSQL database with their table counts and sizes.
10
- Useful for exploring the database structure and understanding the organization of tables across schemas.`,
11
- tags: {
12
- readOnly: true
13
- }
14
- })
15
- .input(
16
- z.object({
17
- includeSystemSchemas: z
18
- .boolean()
19
- .optional()
20
- .default(false)
21
- .describe('Include system schemas (pg_catalog, information_schema, pg_toast)')
22
- })
23
- )
24
- .output(
25
- z.object({
26
- schemas: z
27
- .array(
28
- z.object({
29
- schemaName: z.string().describe('Name of the schema'),
30
- schemaOwner: z.string().describe('Owner of the schema'),
31
- tableCount: z.number().describe('Number of tables in the schema'),
32
- sizeFormatted: z
33
- .string()
34
- .nullable()
35
- .describe('Total size of all tables in the schema')
36
- })
37
- )
38
- .describe('List of schemas in the database'),
39
- totalCount: z.number().describe('Total number of schemas found')
40
- })
41
- )
42
- .handleInvocation(async ctx => {
43
- let client = createClient(ctx.auth, ctx.config);
44
-
45
- let systemFilter = ctx.input.includeSystemSchemas
46
- ? ''
47
- : `WHERE n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND n.nspname NOT LIKE 'pg_temp_%' AND n.nspname NOT LIKE 'pg_toast_temp_%'`;
48
-
49
- let sql = `
50
- SELECT
51
- n.nspname AS schema_name,
52
- pg_catalog.pg_get_userbyid(n.nspowner) AS schema_owner,
53
- COUNT(c.oid) FILTER (WHERE c.relkind IN ('r', 'p')) AS table_count,
54
- pg_size_pretty(COALESCE(SUM(pg_total_relation_size(c.oid)) FILTER (WHERE c.relkind IN ('r', 'p', 'm')), 0)) AS size_formatted
55
- FROM pg_catalog.pg_namespace n
56
- LEFT JOIN pg_catalog.pg_class c ON c.relnamespace = n.oid AND c.relkind IN ('r', 'p', 'm')
57
- ${systemFilter}
58
- GROUP BY n.nspname, n.nspowner
59
- ORDER BY n.nspname
60
- `;
61
-
62
- ctx.info('Listing database schemas');
63
- let result = await client.query(sql, ctx.config.queryTimeout);
64
-
65
- let schemas = result.rows.map((row: any) => ({
66
- schemaName: row.schema_name as string,
67
- schemaOwner: row.schema_owner as string,
68
- tableCount: Number(row.table_count),
69
- sizeFormatted: row.size_formatted as string | null
70
- }));
71
-
72
- return {
73
- output: {
74
- schemas,
75
- totalCount: schemas.length
76
- },
77
- message: `Found **${schemas.length}** schema(s) in the database.`
78
- };
79
- })
80
- .build();
@@ -1,119 +0,0 @@
1
- import { SlateTool } from '@slates/provider';
2
- import { spec } from '../spec';
3
- import { createClient } from '../lib/helpers';
4
- import { z } from 'zod';
5
-
6
- export let listTables = SlateTool.create(spec, {
7
- name: 'List Tables',
8
- key: 'list_tables',
9
- description: `List all tables in the PostgreSQL database, optionally filtered by schema. Returns table names, schemas, row estimates, and size information.
10
- Also supports listing views and materialized views.`,
11
- tags: {
12
- readOnly: true
13
- }
14
- })
15
- .input(
16
- z.object({
17
- schemaName: z
18
- .string()
19
- .optional()
20
- .describe(
21
- 'Filter tables by schema name. If not specified, uses the default schema from config.'
22
- ),
23
- includeViews: z
24
- .boolean()
25
- .optional()
26
- .default(false)
27
- .describe('Include views and materialized views in the results'),
28
- includeSystemTables: z
29
- .boolean()
30
- .optional()
31
- .default(false)
32
- .describe('Include system tables from pg_catalog and information_schema')
33
- })
34
- )
35
- .output(
36
- z.object({
37
- tables: z
38
- .array(
39
- z.object({
40
- tableName: z.string().describe('Name of the table'),
41
- schemaName: z.string().describe('Schema containing the table'),
42
- tableType: z
43
- .string()
44
- .describe('Type of table (BASE TABLE, VIEW, MATERIALIZED VIEW)'),
45
- estimatedRowCount: z
46
- .number()
47
- .nullable()
48
- .describe('Estimated number of rows from pg_stat'),
49
- sizeBytes: z
50
- .number()
51
- .nullable()
52
- .describe('Table size in bytes including indexes and TOAST'),
53
- sizeFormatted: z.string().nullable().describe('Human-readable table size')
54
- })
55
- )
56
- .describe('List of tables in the database'),
57
- totalCount: z.number().describe('Total number of tables found')
58
- })
59
- )
60
- .handleInvocation(async ctx => {
61
- let client = createClient(ctx.auth, ctx.config);
62
- let schema = ctx.input.schemaName || ctx.config.defaultSchema;
63
-
64
- let typeFilter = `'BASE TABLE'`;
65
- if (ctx.input.includeViews) {
66
- typeFilter = `'BASE TABLE', 'VIEW'`;
67
- }
68
-
69
- let schemaFilter = ctx.input.includeSystemTables
70
- ? ''
71
- : `AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')`;
72
-
73
- if (schema && !ctx.input.includeSystemTables) {
74
- schemaFilter = `AND n.nspname = '${schema.replace(/'/g, "''")}'`;
75
- }
76
-
77
- let sql = `
78
- SELECT
79
- c.relname AS table_name,
80
- n.nspname AS schema_name,
81
- CASE c.relkind
82
- WHEN 'r' THEN 'BASE TABLE'
83
- WHEN 'v' THEN 'VIEW'
84
- WHEN 'm' THEN 'MATERIALIZED VIEW'
85
- WHEN 'f' THEN 'FOREIGN TABLE'
86
- WHEN 'p' THEN 'PARTITIONED TABLE'
87
- END AS table_type,
88
- c.reltuples::bigint AS estimated_row_count,
89
- pg_total_relation_size(c.oid) AS size_bytes,
90
- pg_size_pretty(pg_total_relation_size(c.oid)) AS size_formatted
91
- FROM pg_class c
92
- JOIN pg_namespace n ON n.oid = c.relnamespace
93
- WHERE c.relkind IN (${ctx.input.includeViews ? "'r','v','m','p'" : "'r','p'"})
94
- ${schemaFilter}
95
- ORDER BY n.nspname, c.relname
96
- `;
97
-
98
- ctx.info(`Listing tables in schema: ${schema || '(all schemas)'}`);
99
- let result = await client.query(sql, ctx.config.queryTimeout);
100
-
101
- let tables = result.rows.map((row: any) => ({
102
- tableName: row.table_name as string,
103
- schemaName: row.schema_name as string,
104
- tableType: row.table_type as string,
105
- estimatedRowCount:
106
- row.estimated_row_count != null ? Number(row.estimated_row_count) : null,
107
- sizeBytes: row.size_bytes != null ? Number(row.size_bytes) : null,
108
- sizeFormatted: row.size_formatted as string | null
109
- }));
110
-
111
- return {
112
- output: {
113
- tables,
114
- totalCount: tables.length
115
- },
116
- message: `Found **${tables.length}** table(s) in ${schema ? `schema \`${schema}\`` : 'the database'}.`
117
- };
118
- })
119
- .build();