@neverinfamous/postgres-mcp 1.2.0 → 2.0.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 +202 -148
- package/dist/__tests__/benchmarks/codemode.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/codemode.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/codemode.bench.js +159 -0
- package/dist/__tests__/benchmarks/codemode.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/connection-pool.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/connection-pool.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/connection-pool.bench.js +123 -0
- package/dist/__tests__/benchmarks/connection-pool.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/handler-dispatch.bench.d.ts +11 -0
- package/dist/__tests__/benchmarks/handler-dispatch.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/handler-dispatch.bench.js +199 -0
- package/dist/__tests__/benchmarks/handler-dispatch.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/logger-sanitization.bench.d.ts +15 -0
- package/dist/__tests__/benchmarks/logger-sanitization.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/logger-sanitization.bench.js +155 -0
- package/dist/__tests__/benchmarks/logger-sanitization.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/resource-prompts.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/resource-prompts.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/resource-prompts.bench.js +181 -0
- package/dist/__tests__/benchmarks/resource-prompts.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/schema-parsing.bench.d.ts +11 -0
- package/dist/__tests__/benchmarks/schema-parsing.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/schema-parsing.bench.js +209 -0
- package/dist/__tests__/benchmarks/schema-parsing.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/tool-filtering.bench.d.ts +9 -0
- package/dist/__tests__/benchmarks/tool-filtering.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/tool-filtering.bench.js +83 -0
- package/dist/__tests__/benchmarks/tool-filtering.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/transport-auth.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/transport-auth.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/transport-auth.bench.js +128 -0
- package/dist/__tests__/benchmarks/transport-auth.bench.js.map +1 -0
- package/dist/__tests__/benchmarks/utilities.bench.d.ts +10 -0
- package/dist/__tests__/benchmarks/utilities.bench.d.ts.map +1 -0
- package/dist/__tests__/benchmarks/utilities.bench.js +164 -0
- package/dist/__tests__/benchmarks/utilities.bench.js.map +1 -0
- package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
- package/dist/adapters/DatabaseAdapter.js +12 -0
- package/dist/adapters/DatabaseAdapter.js.map +1 -1
- package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
- package/dist/adapters/postgresql/PostgresAdapter.js +56 -3
- package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
- package/dist/adapters/postgresql/prompts/ltree.js +2 -2
- package/dist/adapters/postgresql/prompts/ltree.js.map +1 -1
- package/dist/adapters/postgresql/schemas/admin.d.ts +10 -5
- package/dist/adapters/postgresql/schemas/admin.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/admin.js +10 -5
- package/dist/adapters/postgresql/schemas/admin.js.map +1 -1
- package/dist/adapters/postgresql/schemas/backup.d.ts +45 -27
- package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/backup.js +64 -26
- package/dist/adapters/postgresql/schemas/backup.js.map +1 -1
- package/dist/adapters/postgresql/schemas/core.d.ts +53 -19
- package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/core.js +61 -17
- package/dist/adapters/postgresql/schemas/core.js.map +1 -1
- package/dist/adapters/postgresql/schemas/cron.d.ts +51 -32
- package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/cron.js +64 -44
- package/dist/adapters/postgresql/schemas/cron.js.map +1 -1
- package/dist/adapters/postgresql/schemas/extensions.d.ts +224 -110
- package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/extensions.js +245 -96
- package/dist/adapters/postgresql/schemas/extensions.js.map +1 -1
- package/dist/adapters/postgresql/schemas/index.d.ts +7 -6
- package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/index.js +16 -8
- package/dist/adapters/postgresql/schemas/index.js.map +1 -1
- package/dist/adapters/postgresql/schemas/introspection.d.ts +445 -0
- package/dist/adapters/postgresql/schemas/introspection.d.ts.map +1 -0
- package/dist/adapters/postgresql/schemas/introspection.js +478 -0
- package/dist/adapters/postgresql/schemas/introspection.js.map +1 -0
- package/dist/adapters/postgresql/schemas/jsonb.d.ts +102 -42
- package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/jsonb.js +125 -30
- package/dist/adapters/postgresql/schemas/jsonb.js.map +1 -1
- package/dist/adapters/postgresql/schemas/monitoring.d.ts +69 -36
- package/dist/adapters/postgresql/schemas/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/monitoring.js +98 -40
- package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/schemas/partitioning.d.ts +21 -24
- package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/partitioning.js +26 -14
- package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
- package/dist/adapters/postgresql/schemas/partman.d.ts +69 -0
- package/dist/adapters/postgresql/schemas/partman.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/partman.js +46 -33
- package/dist/adapters/postgresql/schemas/partman.js.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.d.ts +97 -49
- package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.js +139 -34
- package/dist/adapters/postgresql/schemas/performance.js.map +1 -1
- package/dist/adapters/postgresql/schemas/postgis.d.ts +20 -0
- package/dist/adapters/postgresql/schemas/postgis.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/postgis.js +40 -0
- package/dist/adapters/postgresql/schemas/postgis.js.map +1 -1
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +50 -30
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/schema-mgmt.js +105 -33
- package/dist/adapters/postgresql/schemas/schema-mgmt.js.map +1 -1
- package/dist/adapters/postgresql/schemas/stats.d.ts +33 -20
- package/dist/adapters/postgresql/schemas/stats.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/stats.js +36 -20
- package/dist/adapters/postgresql/schemas/stats.js.map +1 -1
- package/dist/adapters/postgresql/schemas/text-search.d.ts +34 -19
- package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/text-search.js +52 -13
- package/dist/adapters/postgresql/schemas/text-search.js.map +1 -1
- package/dist/adapters/postgresql/tools/admin.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/admin.js +272 -186
- package/dist/adapters/postgresql/tools/admin.js.map +1 -1
- package/dist/adapters/postgresql/tools/backup/dump.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/backup/dump.js +376 -350
- package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
- package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/citext.js +333 -243
- package/dist/adapters/postgresql/tools/citext.js.map +1 -1
- package/dist/adapters/postgresql/tools/codemode/index.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/codemode/index.js +2 -11
- package/dist/adapters/postgresql/tools/codemode/index.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/convenience.d.ts +9 -1
- package/dist/adapters/postgresql/tools/core/convenience.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/convenience.js +101 -19
- package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/error-helpers.d.ts +48 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.js +256 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/health.js +18 -4
- package/dist/adapters/postgresql/tools/core/health.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/indexes.js +48 -6
- package/dist/adapters/postgresql/tools/core/indexes.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/objects.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/objects.js +104 -85
- package/dist/adapters/postgresql/tools/core/objects.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/query.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/query.js +100 -42
- package/dist/adapters/postgresql/tools/core/query.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/schemas.d.ts +51 -25
- package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/schemas.js +51 -25
- package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/tables.js +72 -32
- package/dist/adapters/postgresql/tools/core/tables.js.map +1 -1
- package/dist/adapters/postgresql/tools/cron.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/cron.js +333 -206
- package/dist/adapters/postgresql/tools/cron.js.map +1 -1
- package/dist/adapters/postgresql/tools/introspection.d.ts +15 -0
- package/dist/adapters/postgresql/tools/introspection.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/introspection.js +1682 -0
- package/dist/adapters/postgresql/tools/introspection.js.map +1 -0
- package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/advanced.js +394 -297
- package/dist/adapters/postgresql/tools/jsonb/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/basic.js +686 -398
- package/dist/adapters/postgresql/tools/jsonb/basic.js.map +1 -1
- package/dist/adapters/postgresql/tools/kcache.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/kcache.js +278 -246
- package/dist/adapters/postgresql/tools/kcache.js.map +1 -1
- package/dist/adapters/postgresql/tools/ltree.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/ltree.js +137 -38
- package/dist/adapters/postgresql/tools/ltree.js.map +1 -1
- package/dist/adapters/postgresql/tools/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/monitoring.js +86 -55
- package/dist/adapters/postgresql/tools/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/tools/partitioning.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partitioning.js +79 -15
- package/dist/adapters/postgresql/tools/partitioning.js.map +1 -1
- package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partman/management.js +43 -56
- package/dist/adapters/postgresql/tools/partman/management.js.map +1 -1
- package/dist/adapters/postgresql/tools/partman/operations.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partman/operations.js +137 -24
- package/dist/adapters/postgresql/tools/partman/operations.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/analysis.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/analysis.js +276 -165
- package/dist/adapters/postgresql/tools/performance/analysis.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/explain.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/explain.js +61 -21
- package/dist/adapters/postgresql/tools/performance/explain.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/monitoring.js +52 -12
- package/dist/adapters/postgresql/tools/performance/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/optimization.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/optimization.js +92 -81
- package/dist/adapters/postgresql/tools/performance/optimization.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/stats.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/stats.js +182 -60
- package/dist/adapters/postgresql/tools/performance/stats.js.map +1 -1
- package/dist/adapters/postgresql/tools/pgcrypto.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/pgcrypto.js +277 -102
- package/dist/adapters/postgresql/tools/pgcrypto.js.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/advanced.js +298 -230
- package/dist/adapters/postgresql/tools/postgis/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/basic.js +370 -251
- package/dist/adapters/postgresql/tools/postgis/basic.js.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/standalone.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/standalone.js +135 -51
- package/dist/adapters/postgresql/tools/postgis/standalone.js.map +1 -1
- package/dist/adapters/postgresql/tools/schema.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/schema.js +580 -233
- package/dist/adapters/postgresql/tools/schema.js.map +1 -1
- package/dist/adapters/postgresql/tools/stats/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/stats/advanced.js +567 -506
- package/dist/adapters/postgresql/tools/stats/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/stats/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/stats/basic.js +340 -316
- package/dist/adapters/postgresql/tools/stats/basic.js.map +1 -1
- package/dist/adapters/postgresql/tools/text.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/text.js +690 -337
- package/dist/adapters/postgresql/tools/text.js.map +1 -1
- package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/transactions.js +157 -50
- package/dist/adapters/postgresql/tools/transactions.js.map +1 -1
- package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/vector/advanced.js +18 -0
- package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.js +100 -53
- package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -1
- package/dist/auth/auth-context.d.ts +28 -0
- package/dist/auth/auth-context.d.ts.map +1 -0
- package/dist/auth/auth-context.js +37 -0
- package/dist/auth/auth-context.js.map +1 -0
- package/dist/auth/scope-map.d.ts +20 -0
- package/dist/auth/scope-map.d.ts.map +1 -0
- package/dist/auth/scope-map.js +40 -0
- package/dist/auth/scope-map.js.map +1 -0
- package/dist/auth/scopes.d.ts.map +1 -1
- package/dist/auth/scopes.js +2 -0
- package/dist/auth/scopes.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/codemode/api.d.ts +1 -0
- package/dist/codemode/api.d.ts.map +1 -1
- package/dist/codemode/api.js +35 -1
- package/dist/codemode/api.js.map +1 -1
- package/dist/codemode/index.d.ts +0 -2
- package/dist/codemode/index.d.ts.map +1 -1
- package/dist/codemode/index.js +0 -4
- package/dist/codemode/index.js.map +1 -1
- package/dist/codemode/sandbox.d.ts +14 -1
- package/dist/codemode/sandbox.d.ts.map +1 -1
- package/dist/codemode/sandbox.js +58 -19
- package/dist/codemode/sandbox.js.map +1 -1
- package/dist/codemode/types.d.ts.map +1 -1
- package/dist/codemode/types.js +3 -0
- package/dist/codemode/types.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +5 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +117 -31
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/filtering/ToolConstants.d.ts +22 -19
- package/dist/filtering/ToolConstants.d.ts.map +1 -1
- package/dist/filtering/ToolConstants.js +48 -37
- package/dist/filtering/ToolConstants.js.map +1 -1
- package/dist/filtering/ToolFilter.d.ts.map +1 -1
- package/dist/filtering/ToolFilter.js +10 -13
- package/dist/filtering/ToolFilter.js.map +1 -1
- package/dist/pool/ConnectionPool.js +1 -1
- package/dist/pool/ConnectionPool.js.map +1 -1
- package/dist/transports/http.d.ts +1 -0
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +75 -21
- package/dist/transports/http.js.map +1 -1
- package/dist/types/filtering.d.ts +2 -2
- package/dist/types/filtering.d.ts.map +1 -1
- package/dist/utils/icons.d.ts.map +1 -1
- package/dist/utils/icons.js +5 -0
- package/dist/utils/icons.js.map +1 -1
- package/dist/utils/where-clause.d.ts.map +1 -1
- package/dist/utils/where-clause.js +24 -0
- package/dist/utils/where-clause.js.map +1 -1
- package/package.json +20 -13
- package/dist/codemode/sandbox-factory.d.ts +0 -72
- package/dist/codemode/sandbox-factory.d.ts.map +0 -1
- package/dist/codemode/sandbox-factory.js +0 -88
- package/dist/codemode/sandbox-factory.js.map +0 -1
- package/dist/codemode/worker-sandbox.d.ts +0 -82
- package/dist/codemode/worker-sandbox.d.ts.map +0 -1
- package/dist/codemode/worker-sandbox.js +0 -244
- package/dist/codemode/worker-sandbox.js.map +0 -1
- package/dist/codemode/worker-script.d.ts +0 -8
- package/dist/codemode/worker-script.d.ts.map +0 -1
- package/dist/codemode/worker-script.js +0 -113
- package/dist/codemode/worker-script.js.map +0 -1
|
@@ -8,6 +8,7 @@ import { z } from "zod";
|
|
|
8
8
|
import { readOnly, write, destructive } from "../../../utils/annotations.js";
|
|
9
9
|
import { getToolIcons } from "../../../utils/icons.js";
|
|
10
10
|
import { sanitizeIdentifier } from "../../../utils/identifiers.js";
|
|
11
|
+
import { formatPostgresError } from "./core/error-helpers.js";
|
|
11
12
|
import { CreateSchemaSchema, DropSchemaSchema, CreateSequenceSchemaBase, CreateSequenceSchema, DropSequenceSchemaBase, DropSequenceSchema, CreateViewSchemaBase, CreateViewSchema, DropViewSchemaBase, DropViewSchema, ListFunctionsSchemaBase, ListFunctionsSchema,
|
|
12
13
|
// Output schemas
|
|
13
14
|
ListSchemasOutputSchema, CreateSchemaOutputSchema, DropSchemaOutputSchema, ListSequencesOutputSchema, CreateSequenceOutputSchema, DropSequenceOutputSchema, ListViewsOutputSchema, CreateViewOutputSchema, DropViewOutputSchema, ListFunctionsOutputSchema, ListTriggersOutputSchema, ListConstraintsOutputSchema, } from "../schemas/index.js";
|
|
@@ -18,6 +19,7 @@ ListSchemasOutputSchema, CreateSchemaOutputSchema, DropSchemaOutputSchema, ListS
|
|
|
18
19
|
*/
|
|
19
20
|
const EXTENSION_ALIASES = {
|
|
20
21
|
pgvector: "vector",
|
|
22
|
+
vector: "vector",
|
|
21
23
|
partman: "pg_partman",
|
|
22
24
|
fuzzymatch: "fuzzystrmatch",
|
|
23
25
|
fuzzy: "fuzzystrmatch",
|
|
@@ -66,25 +68,47 @@ function createCreateSchemaTool(adapter) {
|
|
|
66
68
|
annotations: write("Create Schema"),
|
|
67
69
|
icons: getToolIcons("schema", write("Create Schema")),
|
|
68
70
|
handler: async (params, _context) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
try {
|
|
72
|
+
const { name, authorization, ifNotExists } = CreateSchemaSchema.parse(params);
|
|
73
|
+
// Check if schema already exists when ifNotExists is true
|
|
74
|
+
let alreadyExisted;
|
|
75
|
+
if (ifNotExists === true) {
|
|
76
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [name]);
|
|
77
|
+
alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
|
|
78
|
+
}
|
|
79
|
+
const ifNotExistsClause = ifNotExists ? "IF NOT EXISTS " : "";
|
|
80
|
+
const schemaName = sanitizeIdentifier(name);
|
|
81
|
+
const authClause = authorization
|
|
82
|
+
? ` AUTHORIZATION ${sanitizeIdentifier(authorization)}`
|
|
83
|
+
: "";
|
|
84
|
+
const sql = `CREATE SCHEMA ${ifNotExistsClause}${schemaName}${authClause}`;
|
|
85
|
+
try {
|
|
86
|
+
await adapter.executeQuery(sql);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: formatPostgresError(error, {
|
|
92
|
+
tool: "pg_create_schema",
|
|
93
|
+
schema: name,
|
|
94
|
+
objectType: "schema",
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const result = { success: true, schema: name };
|
|
99
|
+
if (alreadyExisted !== undefined) {
|
|
100
|
+
result["alreadyExisted"] = alreadyExisted;
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
75
103
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const result = { success: true, schema: name };
|
|
84
|
-
if (alreadyExisted !== undefined) {
|
|
85
|
-
result["alreadyExisted"] = alreadyExisted;
|
|
104
|
+
catch (error) {
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
error: error instanceof z.ZodError
|
|
108
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
109
|
+
: formatPostgresError(error, { tool: "pg_create_schema" }),
|
|
110
|
+
};
|
|
86
111
|
}
|
|
87
|
-
return result;
|
|
88
112
|
},
|
|
89
113
|
};
|
|
90
114
|
}
|
|
@@ -98,23 +122,44 @@ function createDropSchemaTool(adapter) {
|
|
|
98
122
|
annotations: destructive("Drop Schema"),
|
|
99
123
|
icons: getToolIcons("schema", destructive("Drop Schema")),
|
|
100
124
|
handler: async (params, _context) => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
try {
|
|
126
|
+
const { name, cascade, ifExists } = DropSchemaSchema.parse(params);
|
|
127
|
+
// Check if schema exists before dropping (for accurate response)
|
|
128
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [name]);
|
|
129
|
+
const existed = (existsResult.rows?.length ?? 0) > 0;
|
|
130
|
+
const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
|
|
131
|
+
const cascadeClause = cascade === true ? " CASCADE" : "";
|
|
132
|
+
const schemaName = sanitizeIdentifier(name);
|
|
133
|
+
const sql = `DROP SCHEMA ${ifExistsClause}${schemaName}${cascadeClause}`;
|
|
134
|
+
try {
|
|
135
|
+
await adapter.executeQuery(sql);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
error: formatPostgresError(error, {
|
|
141
|
+
tool: "pg_drop_schema",
|
|
142
|
+
schema: name,
|
|
143
|
+
}),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
schema: name,
|
|
149
|
+
existed,
|
|
150
|
+
note: existed
|
|
151
|
+
? undefined
|
|
152
|
+
: `Schema '${name}' did not exist (ifExists: true)`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
success: false,
|
|
158
|
+
error: error instanceof z.ZodError
|
|
159
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
160
|
+
: formatPostgresError(error, { tool: "pg_drop_schema" }),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
118
163
|
},
|
|
119
164
|
};
|
|
120
165
|
}
|
|
@@ -126,6 +171,10 @@ function createListSequencesTool(adapter) {
|
|
|
126
171
|
inputSchema: z
|
|
127
172
|
.object({
|
|
128
173
|
schema: z.string().optional(),
|
|
174
|
+
limit: z
|
|
175
|
+
.number()
|
|
176
|
+
.optional()
|
|
177
|
+
.describe("Maximum number of sequences to return (default: 50). Use 0 for all."),
|
|
129
178
|
})
|
|
130
179
|
.default({}),
|
|
131
180
|
outputSchema: ListSequencesOutputSchema,
|
|
@@ -133,9 +182,24 @@ function createListSequencesTool(adapter) {
|
|
|
133
182
|
icons: getToolIcons("schema", readOnly("List Sequences")),
|
|
134
183
|
handler: async (params, _context) => {
|
|
135
184
|
const parsed = (params ?? {});
|
|
185
|
+
const queryParams = [];
|
|
186
|
+
// Validate schema existence when filtering by schema
|
|
187
|
+
if (parsed.schema) {
|
|
188
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [parsed.schema]);
|
|
189
|
+
if ((schemaCheck.rows?.length ?? 0) === 0) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
error: `Schema '${parsed.schema}' does not exist. Use pg_list_schemas to see available schemas.`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
136
196
|
const schemaClause = parsed.schema
|
|
137
|
-
?
|
|
197
|
+
? (queryParams.push(parsed.schema),
|
|
198
|
+
`AND n.nspname = $${String(queryParams.length)}`)
|
|
138
199
|
: "";
|
|
200
|
+
// Default limit: 50, 0 = no limit
|
|
201
|
+
const limitVal = parsed.limit ?? 50;
|
|
202
|
+
const limitClause = limitVal > 0 ? `LIMIT ${String(limitVal + 1)}` : "";
|
|
139
203
|
// Use subquery for owned_by to avoid duplicate rows from JOINs
|
|
140
204
|
const sql = `SELECT n.nspname as schema, c.relname as name,
|
|
141
205
|
(SELECT tc.relname || '.' || a.attname
|
|
@@ -149,9 +213,44 @@ function createListSequencesTool(adapter) {
|
|
|
149
213
|
WHERE c.relkind = 'S'
|
|
150
214
|
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
151
215
|
${schemaClause}
|
|
152
|
-
ORDER BY n.nspname, c.relname
|
|
153
|
-
|
|
154
|
-
|
|
216
|
+
ORDER BY n.nspname, c.relname
|
|
217
|
+
${limitClause}`;
|
|
218
|
+
const result = queryParams.length > 0
|
|
219
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
220
|
+
: await adapter.executeQuery(sql);
|
|
221
|
+
let sequences = result.rows ?? [];
|
|
222
|
+
// Check if there are more results than the limit
|
|
223
|
+
const hasMore = limitVal > 0 && sequences.length > limitVal;
|
|
224
|
+
if (hasMore) {
|
|
225
|
+
sequences = sequences.slice(0, limitVal);
|
|
226
|
+
}
|
|
227
|
+
const response = {
|
|
228
|
+
sequences,
|
|
229
|
+
count: sequences.length,
|
|
230
|
+
};
|
|
231
|
+
// Always include truncated field for consistent response structure
|
|
232
|
+
response["truncated"] = hasMore;
|
|
233
|
+
if (hasMore) {
|
|
234
|
+
// Get total count
|
|
235
|
+
const countParams = [];
|
|
236
|
+
const countSchemaClause = parsed.schema
|
|
237
|
+
? (countParams.push(parsed.schema),
|
|
238
|
+
`AND n.nspname = $${String(countParams.length)}`)
|
|
239
|
+
: "";
|
|
240
|
+
const countSql = `SELECT COUNT(*)::int as total FROM pg_class c
|
|
241
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
242
|
+
WHERE c.relkind = 'S'
|
|
243
|
+
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
244
|
+
${countSchemaClause}`;
|
|
245
|
+
const countResult = countParams.length > 0
|
|
246
|
+
? await adapter.executeQuery(countSql, countParams)
|
|
247
|
+
: await adapter.executeQuery(countSql);
|
|
248
|
+
response["totalCount"] =
|
|
249
|
+
countResult.rows?.[0]?.["total"] ?? sequences.length;
|
|
250
|
+
response["note"] =
|
|
251
|
+
`Results limited to ${String(limitVal)}. Use 'limit: 0' for all sequences.`;
|
|
252
|
+
}
|
|
253
|
+
return response;
|
|
155
254
|
},
|
|
156
255
|
};
|
|
157
256
|
}
|
|
@@ -165,44 +264,78 @@ function createCreateSequenceTool(adapter) {
|
|
|
165
264
|
annotations: write("Create Sequence"),
|
|
166
265
|
icons: getToolIcons("schema", write("Create Sequence")),
|
|
167
266
|
handler: async (params, _context) => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
267
|
+
try {
|
|
268
|
+
const { name, schema, start, increment, minValue, maxValue, cache, cycle, ownedBy, ifNotExists, } = CreateSequenceSchema.parse(params);
|
|
269
|
+
const schemaName = schema ?? "public";
|
|
270
|
+
// Check if sequence already exists when ifNotExists is true
|
|
271
|
+
let alreadyExisted;
|
|
272
|
+
if (ifNotExists === true) {
|
|
273
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'S' AND n.nspname = $1 AND c.relname = $2`, [schemaName, name]);
|
|
274
|
+
alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
|
|
275
|
+
}
|
|
276
|
+
const schemaPrefix = schema ? `${sanitizeIdentifier(schema)}.` : "";
|
|
277
|
+
const ifNotExistsClause = ifNotExists === true ? "IF NOT EXISTS " : "";
|
|
278
|
+
const parts = [
|
|
279
|
+
`CREATE SEQUENCE ${ifNotExistsClause}${schemaPrefix}${sanitizeIdentifier(name)}`,
|
|
280
|
+
];
|
|
281
|
+
if (start !== undefined)
|
|
282
|
+
parts.push(`START WITH ${String(start)}`);
|
|
283
|
+
if (increment !== undefined)
|
|
284
|
+
parts.push(`INCREMENT BY ${String(increment)}`);
|
|
285
|
+
if (minValue !== undefined)
|
|
286
|
+
parts.push(`MINVALUE ${String(minValue)}`);
|
|
287
|
+
if (maxValue !== undefined)
|
|
288
|
+
parts.push(`MAXVALUE ${String(maxValue)}`);
|
|
289
|
+
if (cache !== undefined)
|
|
290
|
+
parts.push(`CACHE ${String(cache)}`);
|
|
291
|
+
if (cycle)
|
|
292
|
+
parts.push("CYCLE");
|
|
293
|
+
if (ownedBy !== undefined) {
|
|
294
|
+
// Validate and sanitize ownedBy: table.column or schema.table.column
|
|
295
|
+
const ownedByParts = ownedBy.split(".");
|
|
296
|
+
if (ownedByParts.length < 2 || ownedByParts.length > 3) {
|
|
297
|
+
return {
|
|
298
|
+
success: false,
|
|
299
|
+
error: `Invalid ownedBy format: '${ownedBy}'. Expected 'table.column' or 'schema.table.column'.`,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
const sanitizedOwnedBy = ownedByParts
|
|
303
|
+
.map((p) => sanitizeIdentifier(p))
|
|
304
|
+
.join(".");
|
|
305
|
+
parts.push(`OWNED BY ${sanitizedOwnedBy}`);
|
|
306
|
+
}
|
|
307
|
+
const sql = parts.join(" ");
|
|
308
|
+
try {
|
|
309
|
+
await adapter.executeQuery(sql);
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
return {
|
|
313
|
+
success: false,
|
|
314
|
+
error: formatPostgresError(error, {
|
|
315
|
+
tool: "pg_create_sequence",
|
|
316
|
+
objectType: "sequence",
|
|
317
|
+
...(schema !== undefined && { schema }),
|
|
318
|
+
}),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const result = {
|
|
322
|
+
success: true,
|
|
323
|
+
sequence: `${schemaName}.${name}`,
|
|
324
|
+
ifNotExists: ifNotExists ?? false,
|
|
325
|
+
};
|
|
326
|
+
if (alreadyExisted !== undefined) {
|
|
327
|
+
result["alreadyExisted"] = alreadyExisted;
|
|
328
|
+
}
|
|
329
|
+
return result;
|
|
175
330
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (increment !== undefined)
|
|
184
|
-
parts.push(`INCREMENT BY ${String(increment)}`);
|
|
185
|
-
if (minValue !== undefined)
|
|
186
|
-
parts.push(`MINVALUE ${String(minValue)}`);
|
|
187
|
-
if (maxValue !== undefined)
|
|
188
|
-
parts.push(`MAXVALUE ${String(maxValue)}`);
|
|
189
|
-
if (cache !== undefined)
|
|
190
|
-
parts.push(`CACHE ${String(cache)}`);
|
|
191
|
-
if (cycle)
|
|
192
|
-
parts.push("CYCLE");
|
|
193
|
-
if (ownedBy !== undefined)
|
|
194
|
-
parts.push(`OWNED BY ${ownedBy}`);
|
|
195
|
-
const sql = parts.join(" ");
|
|
196
|
-
await adapter.executeQuery(sql);
|
|
197
|
-
const result = {
|
|
198
|
-
success: true,
|
|
199
|
-
sequence: `${schemaName}.${name}`,
|
|
200
|
-
ifNotExists: ifNotExists ?? false,
|
|
201
|
-
};
|
|
202
|
-
if (alreadyExisted !== undefined) {
|
|
203
|
-
result["alreadyExisted"] = alreadyExisted;
|
|
331
|
+
catch (error) {
|
|
332
|
+
return {
|
|
333
|
+
success: false,
|
|
334
|
+
error: error instanceof z.ZodError
|
|
335
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
336
|
+
: formatPostgresError(error, { tool: "pg_create_sequence" }),
|
|
337
|
+
};
|
|
204
338
|
}
|
|
205
|
-
return result;
|
|
206
339
|
},
|
|
207
340
|
};
|
|
208
341
|
}
|
|
@@ -217,16 +350,37 @@ function createDropSequenceTool(adapter) {
|
|
|
217
350
|
annotations: destructive("Drop Sequence"),
|
|
218
351
|
icons: getToolIcons("schema", destructive("Drop Sequence")),
|
|
219
352
|
handler: async (params, _context) => {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
353
|
+
try {
|
|
354
|
+
const { name, schema, ifExists, cascade } = DropSequenceSchema.parse(params);
|
|
355
|
+
const schemaName = schema ?? "public";
|
|
356
|
+
// Check if sequence exists before dropping (for accurate response)
|
|
357
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'S' AND n.nspname = $1 AND c.relname = $2`, [schemaName, name]);
|
|
358
|
+
const existed = (existsResult.rows?.length ?? 0) > 0;
|
|
359
|
+
const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
|
|
360
|
+
const cascadeClause = cascade === true ? " CASCADE" : "";
|
|
361
|
+
const sql = `DROP SEQUENCE ${ifExistsClause}"${schemaName}"."${name}"${cascadeClause}`;
|
|
362
|
+
try {
|
|
363
|
+
await adapter.executeQuery(sql);
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
return {
|
|
367
|
+
success: false,
|
|
368
|
+
error: formatPostgresError(error, {
|
|
369
|
+
tool: "pg_drop_sequence",
|
|
370
|
+
...(schema !== undefined && { schema }),
|
|
371
|
+
}),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return { success: true, sequence: `${schemaName}.${name}`, existed };
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
return {
|
|
378
|
+
success: false,
|
|
379
|
+
error: error instanceof z.ZodError
|
|
380
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
381
|
+
: formatPostgresError(error, { tool: "pg_drop_sequence" }),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
230
384
|
},
|
|
231
385
|
};
|
|
232
386
|
}
|
|
@@ -252,8 +406,20 @@ function createListViewsTool(adapter) {
|
|
|
252
406
|
icons: getToolIcons("schema", readOnly("List Views")),
|
|
253
407
|
handler: async (params, _context) => {
|
|
254
408
|
const parsed = (params ?? {});
|
|
409
|
+
const queryParams = [];
|
|
410
|
+
// Validate schema existence when filtering by schema
|
|
411
|
+
if (parsed.schema) {
|
|
412
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [parsed.schema]);
|
|
413
|
+
if ((schemaCheck.rows?.length ?? 0) === 0) {
|
|
414
|
+
return {
|
|
415
|
+
success: false,
|
|
416
|
+
error: `Schema '${parsed.schema}' does not exist. Use pg_list_schemas to see available schemas.`,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
255
420
|
const schemaClause = parsed.schema
|
|
256
|
-
?
|
|
421
|
+
? (queryParams.push(parsed.schema),
|
|
422
|
+
`AND n.nspname = $${String(queryParams.length)}`)
|
|
257
423
|
: "";
|
|
258
424
|
const kindClause = parsed.includeMaterialized !== false ? "IN ('v', 'm')" : "= 'v'";
|
|
259
425
|
// Default truncation: 500 chars, 0 = no truncation
|
|
@@ -271,7 +437,9 @@ function createListViewsTool(adapter) {
|
|
|
271
437
|
${schemaClause}
|
|
272
438
|
ORDER BY n.nspname, c.relname
|
|
273
439
|
${limitClause}`;
|
|
274
|
-
const result =
|
|
440
|
+
const result = queryParams.length > 0
|
|
441
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
442
|
+
: await adapter.executeQuery(sql);
|
|
275
443
|
let views = result.rows ?? [];
|
|
276
444
|
// Check if there are more results than the limit
|
|
277
445
|
const hasMore = limitVal > 0 && views.length > limitVal;
|
|
@@ -306,6 +474,22 @@ function createListViewsTool(adapter) {
|
|
|
306
474
|
// Always include truncated field for consistent response structure
|
|
307
475
|
response["truncated"] = hasMore;
|
|
308
476
|
if (hasMore) {
|
|
477
|
+
// Get total count
|
|
478
|
+
const countParams = [];
|
|
479
|
+
const countSchemaClause = parsed.schema
|
|
480
|
+
? (countParams.push(parsed.schema),
|
|
481
|
+
`AND n.nspname = $${String(countParams.length)}`)
|
|
482
|
+
: "";
|
|
483
|
+
const countSql = `SELECT COUNT(*)::int as total FROM pg_class c
|
|
484
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
485
|
+
WHERE c.relkind ${kindClause}
|
|
486
|
+
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
487
|
+
${countSchemaClause}`;
|
|
488
|
+
const countResult = countParams.length > 0
|
|
489
|
+
? await adapter.executeQuery(countSql, countParams)
|
|
490
|
+
: await adapter.executeQuery(countSql);
|
|
491
|
+
response["totalCount"] =
|
|
492
|
+
countResult.rows?.[0]?.["total"] ?? views.length;
|
|
309
493
|
response["note"] =
|
|
310
494
|
`Results limited to ${String(limitVal)}. Use 'limit: 0' for all views.`;
|
|
311
495
|
}
|
|
@@ -323,35 +507,57 @@ function createCreateViewTool(adapter) {
|
|
|
323
507
|
annotations: write("Create View"),
|
|
324
508
|
icons: getToolIcons("schema", write("Create View")),
|
|
325
509
|
handler: async (params, _context) => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
510
|
+
try {
|
|
511
|
+
const { name, schema, query, materialized, orReplace, checkOption } = CreateViewSchema.parse(params);
|
|
512
|
+
const schemaName = schema ?? "public";
|
|
513
|
+
// Check if view already exists when orReplace is true (for informational response)
|
|
514
|
+
let alreadyExisted;
|
|
515
|
+
if (orReplace === true) {
|
|
516
|
+
const relkind = materialized === true ? "m" : "v";
|
|
517
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = $1 AND n.nspname = $2 AND c.relname = $3`, [relkind, schemaName, name]);
|
|
518
|
+
alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
|
|
519
|
+
}
|
|
520
|
+
const schemaPrefix = schema ? `${sanitizeIdentifier(schema)}.` : "";
|
|
521
|
+
const replaceClause = orReplace && !materialized ? "OR REPLACE " : "";
|
|
522
|
+
const matClause = materialized ? "MATERIALIZED " : "";
|
|
523
|
+
const viewName = sanitizeIdentifier(name);
|
|
524
|
+
// WITH CHECK OPTION clause (not available for materialized views)
|
|
525
|
+
let checkClause = "";
|
|
526
|
+
if (checkOption && checkOption !== "none" && !materialized) {
|
|
527
|
+
checkClause = ` WITH ${checkOption.toUpperCase()} CHECK OPTION`;
|
|
528
|
+
}
|
|
529
|
+
const sql = `CREATE ${replaceClause}${matClause}VIEW ${schemaPrefix}${viewName} AS ${query}${checkClause}`;
|
|
530
|
+
try {
|
|
531
|
+
await adapter.executeQuery(sql);
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
return {
|
|
535
|
+
success: false,
|
|
536
|
+
error: formatPostgresError(error, {
|
|
537
|
+
tool: "pg_create_view",
|
|
538
|
+
objectType: "view",
|
|
539
|
+
...(schema !== undefined && { schema }),
|
|
540
|
+
}),
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
const result = {
|
|
544
|
+
success: true,
|
|
545
|
+
view: `${schemaName}.${name}`,
|
|
546
|
+
materialized: !!materialized,
|
|
547
|
+
};
|
|
548
|
+
if (alreadyExisted !== undefined) {
|
|
549
|
+
result["alreadyExisted"] = alreadyExisted;
|
|
550
|
+
}
|
|
551
|
+
return result;
|
|
343
552
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (alreadyExisted !== undefined) {
|
|
352
|
-
result["alreadyExisted"] = alreadyExisted;
|
|
553
|
+
catch (error) {
|
|
554
|
+
return {
|
|
555
|
+
success: false,
|
|
556
|
+
error: error instanceof z.ZodError
|
|
557
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
558
|
+
: formatPostgresError(error, { tool: "pg_create_view" }),
|
|
559
|
+
};
|
|
353
560
|
}
|
|
354
|
-
return result;
|
|
355
561
|
},
|
|
356
562
|
};
|
|
357
563
|
}
|
|
@@ -366,23 +572,44 @@ function createDropViewTool(adapter) {
|
|
|
366
572
|
annotations: destructive("Drop View"),
|
|
367
573
|
icons: getToolIcons("schema", destructive("Drop View")),
|
|
368
574
|
handler: async (params, _context) => {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
575
|
+
try {
|
|
576
|
+
const { name, schema, materialized, ifExists, cascade } = DropViewSchema.parse(params);
|
|
577
|
+
const schemaName = schema ?? "public";
|
|
578
|
+
// Check if view exists before dropping (for accurate response)
|
|
579
|
+
const relkind = materialized === true ? "m" : "v";
|
|
580
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = $1 AND n.nspname = $2 AND c.relname = $3`, [relkind, schemaName, name]);
|
|
581
|
+
const existed = (existsResult.rows?.length ?? 0) > 0;
|
|
582
|
+
const matClause = materialized === true ? "MATERIALIZED " : "";
|
|
583
|
+
const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
|
|
584
|
+
const cascadeClause = cascade === true ? " CASCADE" : "";
|
|
585
|
+
const sql = `DROP ${matClause}VIEW ${ifExistsClause}"${schemaName}"."${name}"${cascadeClause}`;
|
|
586
|
+
try {
|
|
587
|
+
await adapter.executeQuery(sql);
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
return {
|
|
591
|
+
success: false,
|
|
592
|
+
error: formatPostgresError(error, {
|
|
593
|
+
tool: "pg_drop_view",
|
|
594
|
+
...(schema !== undefined && { schema }),
|
|
595
|
+
}),
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
return {
|
|
599
|
+
success: true,
|
|
600
|
+
view: `${schemaName}.${name}`,
|
|
601
|
+
materialized: materialized ?? false,
|
|
602
|
+
existed,
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
catch (error) {
|
|
606
|
+
return {
|
|
607
|
+
success: false,
|
|
608
|
+
error: error instanceof z.ZodError
|
|
609
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
610
|
+
: formatPostgresError(error, { tool: "pg_drop_view" }),
|
|
611
|
+
};
|
|
612
|
+
}
|
|
386
613
|
},
|
|
387
614
|
};
|
|
388
615
|
}
|
|
@@ -397,56 +624,85 @@ function createListFunctionsTool(adapter) {
|
|
|
397
624
|
annotations: readOnly("List Functions"),
|
|
398
625
|
icons: getToolIcons("schema", readOnly("List Functions")),
|
|
399
626
|
handler: async (params, _context) => {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
627
|
+
try {
|
|
628
|
+
// Use full schema with preprocessing for validation
|
|
629
|
+
const parsed = ListFunctionsSchema.parse(params);
|
|
630
|
+
const queryParams = [];
|
|
631
|
+
// Validate schema existence when filtering by schema
|
|
632
|
+
if (parsed.schema !== undefined) {
|
|
633
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [parsed.schema]);
|
|
634
|
+
if ((schemaCheck.rows?.length ?? 0) === 0) {
|
|
635
|
+
return {
|
|
636
|
+
success: false,
|
|
637
|
+
error: `Schema '${parsed.schema}' does not exist. Use pg_list_schemas to see available schemas.`,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
const conditions = [
|
|
642
|
+
"n.nspname NOT IN ('pg_catalog', 'information_schema')",
|
|
643
|
+
];
|
|
644
|
+
if (parsed.schema !== undefined) {
|
|
645
|
+
queryParams.push(parsed.schema);
|
|
646
|
+
conditions.push(`n.nspname = $${String(queryParams.length)}`);
|
|
647
|
+
}
|
|
648
|
+
if (parsed.exclude !== undefined && parsed.exclude.length > 0) {
|
|
649
|
+
// Expand well-known aliases (e.g. "pgvector" -> ["pgvector", "vector"])
|
|
650
|
+
const normalizedExclude = parsed.exclude.flatMap((s) => {
|
|
651
|
+
const alias = EXTENSION_ALIASES[s];
|
|
652
|
+
return alias ? [s, alias] : [s];
|
|
653
|
+
});
|
|
654
|
+
const excludePlaceholders = normalizedExclude.map((s) => {
|
|
655
|
+
queryParams.push(s);
|
|
656
|
+
return `$${String(queryParams.length)}`;
|
|
657
|
+
});
|
|
658
|
+
const excludeList = excludePlaceholders.join(", ");
|
|
659
|
+
// Exclude by schema name
|
|
660
|
+
conditions.push(`n.nspname NOT IN (${excludeList})`);
|
|
661
|
+
// Also exclude extension-owned functions (e.g., ltree functions in public schema)
|
|
662
|
+
conditions.push(`NOT EXISTS (
|
|
663
|
+
SELECT 1 FROM pg_depend d
|
|
664
|
+
JOIN pg_extension e ON d.refobjid = e.oid
|
|
665
|
+
WHERE d.objid = p.oid
|
|
666
|
+
AND d.deptype = 'e'
|
|
667
|
+
AND e.extname IN (${excludeList})
|
|
668
|
+
)`);
|
|
669
|
+
}
|
|
670
|
+
if (parsed.language !== undefined) {
|
|
671
|
+
queryParams.push(parsed.language);
|
|
672
|
+
conditions.push(`l.lanname = $${String(queryParams.length)}`);
|
|
673
|
+
}
|
|
674
|
+
const limitVal = parsed.limit ?? 500;
|
|
675
|
+
const sql = `SELECT n.nspname as schema, p.proname as name,
|
|
676
|
+
pg_get_function_arguments(p.oid) as arguments,
|
|
677
|
+
pg_get_function_result(p.oid) as returns,
|
|
678
|
+
l.lanname as language,
|
|
679
|
+
p.provolatile as volatility
|
|
680
|
+
FROM pg_proc p
|
|
681
|
+
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
682
|
+
JOIN pg_language l ON l.oid = p.prolang
|
|
683
|
+
WHERE ${conditions.join(" AND ")}
|
|
684
|
+
ORDER BY n.nspname, p.proname
|
|
685
|
+
LIMIT ${String(limitVal)}`;
|
|
686
|
+
const result = queryParams.length > 0
|
|
687
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
688
|
+
: await adapter.executeQuery(sql);
|
|
689
|
+
return {
|
|
690
|
+
functions: result.rows,
|
|
691
|
+
count: result.rows?.length ?? 0,
|
|
692
|
+
limit: limitVal,
|
|
693
|
+
note: (result.rows?.length ?? 0) >= limitVal
|
|
694
|
+
? `Results limited to ${String(limitVal)}. Use 'limit' param for more, or 'exclude' to filter out extension schemas.`
|
|
695
|
+
: undefined,
|
|
696
|
+
};
|
|
425
697
|
}
|
|
426
|
-
|
|
427
|
-
|
|
698
|
+
catch (error) {
|
|
699
|
+
return {
|
|
700
|
+
success: false,
|
|
701
|
+
error: error instanceof z.ZodError
|
|
702
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
703
|
+
: formatPostgresError(error, { tool: "pg_list_functions" }),
|
|
704
|
+
};
|
|
428
705
|
}
|
|
429
|
-
const limitVal = parsed.limit ?? 500;
|
|
430
|
-
const sql = `SELECT n.nspname as schema, p.proname as name,
|
|
431
|
-
pg_get_function_arguments(p.oid) as arguments,
|
|
432
|
-
pg_get_function_result(p.oid) as returns,
|
|
433
|
-
l.lanname as language,
|
|
434
|
-
p.provolatile as volatility
|
|
435
|
-
FROM pg_proc p
|
|
436
|
-
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
437
|
-
JOIN pg_language l ON l.oid = p.prolang
|
|
438
|
-
WHERE ${conditions.join(" AND ")}
|
|
439
|
-
ORDER BY n.nspname, p.proname
|
|
440
|
-
LIMIT ${String(limitVal)}`;
|
|
441
|
-
const result = await adapter.executeQuery(sql);
|
|
442
|
-
return {
|
|
443
|
-
functions: result.rows,
|
|
444
|
-
count: result.rows?.length ?? 0,
|
|
445
|
-
limit: limitVal,
|
|
446
|
-
note: (result.rows?.length ?? 0) >= limitVal
|
|
447
|
-
? `Results limited to ${String(limitVal)}. Use 'limit' param for more, or 'exclude' to filter out extension schemas.`
|
|
448
|
-
: undefined,
|
|
449
|
-
};
|
|
450
706
|
},
|
|
451
707
|
};
|
|
452
708
|
}
|
|
@@ -463,31 +719,77 @@ function createListTriggersTool(adapter) {
|
|
|
463
719
|
annotations: readOnly("List Triggers"),
|
|
464
720
|
icons: getToolIcons("schema", readOnly("List Triggers")),
|
|
465
721
|
handler: async (params, _context) => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
722
|
+
try {
|
|
723
|
+
const parsed = (params ?? {});
|
|
724
|
+
// Parse schema.table format
|
|
725
|
+
if (typeof parsed.table === "string" &&
|
|
726
|
+
parsed.table.includes(".") &&
|
|
727
|
+
!parsed.schema) {
|
|
728
|
+
const parts = parsed.table.split(".");
|
|
729
|
+
if (parts.length === 2 && parts[0] && parts[1]) {
|
|
730
|
+
parsed.schema = parts[0];
|
|
731
|
+
parsed.table = parts[1];
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
const schemaName = parsed.schema ?? "public";
|
|
735
|
+
// Validate schema existence when filtering by schema
|
|
736
|
+
if (parsed.schema) {
|
|
737
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [parsed.schema]);
|
|
738
|
+
if ((schemaCheck.rows?.length ?? 0) === 0) {
|
|
739
|
+
return {
|
|
740
|
+
success: false,
|
|
741
|
+
error: `Schema '${parsed.schema}' does not exist. Use pg_list_schemas to see available schemas.`,
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
// Validate table existence when filtering by table
|
|
746
|
+
if (parsed.table) {
|
|
747
|
+
const tableCheck = await adapter.executeQuery(`SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2`, [schemaName, parsed.table]);
|
|
748
|
+
if ((tableCheck.rows?.length ?? 0) === 0) {
|
|
749
|
+
return {
|
|
750
|
+
success: false,
|
|
751
|
+
error: `Table '${schemaName}.${parsed.table}' not found. Use pg_list_tables to see available tables.`,
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const queryParams = [];
|
|
756
|
+
let whereClause = "n.nspname NOT IN ('pg_catalog', 'information_schema')";
|
|
757
|
+
if (parsed.schema) {
|
|
758
|
+
queryParams.push(parsed.schema);
|
|
759
|
+
whereClause += ` AND n.nspname = $${String(queryParams.length)}`;
|
|
760
|
+
}
|
|
761
|
+
if (parsed.table) {
|
|
762
|
+
queryParams.push(parsed.table);
|
|
763
|
+
whereClause += ` AND c.relname = $${String(queryParams.length)}`;
|
|
764
|
+
}
|
|
765
|
+
const sql = `SELECT n.nspname as schema, c.relname as table_name, t.tgname as name,
|
|
766
|
+
CASE t.tgtype::int & 2 WHEN 2 THEN 'BEFORE' ELSE 'AFTER' END as timing,
|
|
767
|
+
array_remove(ARRAY[
|
|
768
|
+
CASE WHEN t.tgtype::int & 4 = 4 THEN 'INSERT' END,
|
|
769
|
+
CASE WHEN t.tgtype::int & 8 = 8 THEN 'DELETE' END,
|
|
770
|
+
CASE WHEN t.tgtype::int & 16 = 16 THEN 'UPDATE' END,
|
|
771
|
+
CASE WHEN t.tgtype::int & 32 = 32 THEN 'TRUNCATE' END
|
|
772
|
+
], NULL) as events,
|
|
773
|
+
p.proname as function_name,
|
|
774
|
+
t.tgenabled != 'D' as enabled
|
|
775
|
+
FROM pg_trigger t
|
|
776
|
+
JOIN pg_class c ON c.oid = t.tgrelid
|
|
777
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
778
|
+
JOIN pg_proc p ON p.oid = t.tgfoid
|
|
779
|
+
WHERE NOT t.tgisinternal
|
|
780
|
+
AND ${whereClause}
|
|
781
|
+
ORDER BY n.nspname, c.relname, t.tgname`;
|
|
782
|
+
const result = queryParams.length > 0
|
|
783
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
784
|
+
: await adapter.executeQuery(sql);
|
|
785
|
+
return { triggers: result.rows, count: result.rows?.length ?? 0 };
|
|
786
|
+
}
|
|
787
|
+
catch (error) {
|
|
788
|
+
return {
|
|
789
|
+
success: false,
|
|
790
|
+
error: formatPostgresError(error, { tool: "pg_list_triggers" }),
|
|
791
|
+
};
|
|
792
|
+
}
|
|
491
793
|
},
|
|
492
794
|
};
|
|
493
795
|
}
|
|
@@ -507,38 +809,83 @@ function createListConstraintsTool(adapter) {
|
|
|
507
809
|
annotations: readOnly("List Constraints"),
|
|
508
810
|
icons: getToolIcons("schema", readOnly("List Constraints")),
|
|
509
811
|
handler: async (params, _context) => {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
812
|
+
try {
|
|
813
|
+
const parsed = (params ?? {});
|
|
814
|
+
// Parse schema.table format
|
|
815
|
+
if (typeof parsed.table === "string" &&
|
|
816
|
+
parsed.table.includes(".") &&
|
|
817
|
+
!parsed.schema) {
|
|
818
|
+
const parts = parsed.table.split(".");
|
|
819
|
+
if (parts.length === 2 && parts[0] && parts[1]) {
|
|
820
|
+
parsed.schema = parts[0];
|
|
821
|
+
parsed.table = parts[1];
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
const schemaName = parsed.schema ?? "public";
|
|
825
|
+
// Validate schema existence when filtering by schema
|
|
826
|
+
if (parsed.schema) {
|
|
827
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [parsed.schema]);
|
|
828
|
+
if ((schemaCheck.rows?.length ?? 0) === 0) {
|
|
829
|
+
return {
|
|
830
|
+
success: false,
|
|
831
|
+
error: `Schema '${parsed.schema}' does not exist. Use pg_list_schemas to see available schemas.`,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// Validate table existence when filtering by table
|
|
836
|
+
if (parsed.table) {
|
|
837
|
+
const tableCheck = await adapter.executeQuery(`SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2`, [schemaName, parsed.table]);
|
|
838
|
+
if ((tableCheck.rows?.length ?? 0) === 0) {
|
|
839
|
+
return {
|
|
840
|
+
success: false,
|
|
841
|
+
error: `Table '${schemaName}.${parsed.table}' not found. Use pg_list_tables to see available tables.`,
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
const queryParams = [];
|
|
846
|
+
let whereClause = "n.nspname NOT IN ('pg_catalog', 'information_schema') AND con.contype != 'n'";
|
|
847
|
+
if (parsed.schema) {
|
|
848
|
+
queryParams.push(parsed.schema);
|
|
849
|
+
whereClause += ` AND n.nspname = $${String(queryParams.length)}`;
|
|
850
|
+
}
|
|
851
|
+
if (parsed.table) {
|
|
852
|
+
queryParams.push(parsed.table);
|
|
853
|
+
whereClause += ` AND c.relname = $${String(queryParams.length)}`;
|
|
854
|
+
}
|
|
855
|
+
if (parsed.type) {
|
|
856
|
+
const typeMap = {
|
|
857
|
+
primary_key: "p",
|
|
858
|
+
foreign_key: "f",
|
|
859
|
+
unique: "u",
|
|
860
|
+
check: "c",
|
|
861
|
+
};
|
|
862
|
+
queryParams.push(typeMap[parsed.type] ?? "");
|
|
863
|
+
whereClause += ` AND con.contype = $${String(queryParams.length)}`;
|
|
864
|
+
}
|
|
865
|
+
const sql = `SELECT n.nspname as schema, c.relname as table_name, con.conname as name,
|
|
866
|
+
CASE con.contype
|
|
867
|
+
WHEN 'p' THEN 'primary_key'
|
|
868
|
+
WHEN 'f' THEN 'foreign_key'
|
|
869
|
+
WHEN 'u' THEN 'unique'
|
|
870
|
+
WHEN 'c' THEN 'check'
|
|
871
|
+
END as type,
|
|
872
|
+
pg_get_constraintdef(con.oid) as definition
|
|
873
|
+
FROM pg_constraint con
|
|
874
|
+
JOIN pg_class c ON c.oid = con.conrelid
|
|
875
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
876
|
+
WHERE ${whereClause}
|
|
877
|
+
ORDER BY n.nspname, c.relname, con.conname`;
|
|
878
|
+
const result = queryParams.length > 0
|
|
879
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
880
|
+
: await adapter.executeQuery(sql);
|
|
881
|
+
return { constraints: result.rows, count: result.rows?.length ?? 0 };
|
|
882
|
+
}
|
|
883
|
+
catch (error) {
|
|
884
|
+
return {
|
|
885
|
+
success: false,
|
|
886
|
+
error: formatPostgresError(error, { tool: "pg_list_constraints" }),
|
|
522
887
|
};
|
|
523
|
-
whereClause += ` AND con.contype = '${typeMap[parsed.type] ?? ""}'`;
|
|
524
888
|
}
|
|
525
|
-
const sql = `SELECT n.nspname as schema, c.relname as table_name, con.conname as name,
|
|
526
|
-
CASE con.contype
|
|
527
|
-
WHEN 'p' THEN 'primary_key'
|
|
528
|
-
WHEN 'f' THEN 'foreign_key'
|
|
529
|
-
WHEN 'u' THEN 'unique'
|
|
530
|
-
WHEN 'c' THEN 'check'
|
|
531
|
-
WHEN 'n' THEN 'not_null'
|
|
532
|
-
ELSE con.contype
|
|
533
|
-
END as type,
|
|
534
|
-
pg_get_constraintdef(con.oid) as definition
|
|
535
|
-
FROM pg_constraint con
|
|
536
|
-
JOIN pg_class c ON c.oid = con.conrelid
|
|
537
|
-
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
538
|
-
WHERE ${whereClause}
|
|
539
|
-
ORDER BY n.nspname, c.relname, con.conname`;
|
|
540
|
-
const result = await adapter.executeQuery(sql);
|
|
541
|
-
return { constraints: result.rows, count: result.rows?.length ?? 0 };
|
|
542
889
|
},
|
|
543
890
|
};
|
|
544
891
|
}
|