@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
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { readOnly, write } from "../../../utils/annotations.js";
|
|
14
|
+
import { parsePostgresError } from "./core/error-helpers.js";
|
|
14
15
|
import { getToolIcons } from "../../../utils/icons.js";
|
|
15
16
|
import { CitextConvertColumnSchema, CitextConvertColumnSchemaBase, CitextListColumnsSchema, CitextListColumnsSchemaBase, CitextAnalyzeCandidatesSchema, CitextAnalyzeCandidatesSchemaBase, CitextSchemaAdvisorSchema, CitextSchemaAdvisorSchemaBase,
|
|
16
17
|
// Output schemas
|
|
@@ -42,12 +43,19 @@ citext is ideal for emails, usernames, and other identifiers where case shouldn'
|
|
|
42
43
|
annotations: write("Create Citext Extension"),
|
|
43
44
|
icons: getToolIcons("citext", write("Create Citext Extension")),
|
|
44
45
|
handler: async (_params, _context) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
try {
|
|
47
|
+
await adapter.executeQuery("CREATE EXTENSION IF NOT EXISTS citext");
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
message: "citext extension enabled",
|
|
51
|
+
usage: "Create columns with type CITEXT instead of TEXT for case-insensitive comparisons",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw parsePostgresError(error, {
|
|
56
|
+
tool: "pg_citext_create_extension",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
51
59
|
},
|
|
52
60
|
};
|
|
53
61
|
}
|
|
@@ -66,61 +74,79 @@ Note: If views depend on this column, you must drop and recreate them manually b
|
|
|
66
74
|
annotations: write("Convert to Citext"),
|
|
67
75
|
icons: getToolIcons("citext", write("Convert to Citext")),
|
|
68
76
|
handler: async (params, _context) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
try {
|
|
78
|
+
const parsed = CitextConvertColumnSchema.parse(params ?? {});
|
|
79
|
+
const { table, column, schema: schemaOpt } = parsed;
|
|
80
|
+
const schemaName = schemaOpt ?? "public";
|
|
81
|
+
const qualifiedTable = `"${schemaName}"."${table}"`;
|
|
82
|
+
const extCheck = await adapter.executeQuery(`
|
|
74
83
|
SELECT EXISTS(
|
|
75
84
|
SELECT 1 FROM pg_extension WHERE extname = 'citext'
|
|
76
85
|
) as installed
|
|
77
86
|
`);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
const hasExt = extCheck.rows?.[0]?.["installed"] ?? false;
|
|
88
|
+
if (!hasExt) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: "citext extension is not installed. Run pg_citext_create_extension first.",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// Check if table exists before checking column
|
|
95
|
+
const tableCheck = await adapter.executeQuery(`
|
|
96
|
+
SELECT 1 FROM information_schema.tables
|
|
97
|
+
WHERE table_schema = $1 AND table_name = $2
|
|
98
|
+
`, [schemaName, table]);
|
|
99
|
+
if (!tableCheck.rows || tableCheck.rows.length === 0) {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
error: `Table ${qualifiedTable} does not exist. Verify the table name and schema.`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const colCheck = await adapter.executeQuery(`
|
|
83
106
|
SELECT data_type, udt_name
|
|
84
|
-
FROM information_schema.columns
|
|
85
|
-
WHERE table_schema = $1
|
|
86
|
-
AND table_name = $2
|
|
107
|
+
FROM information_schema.columns
|
|
108
|
+
WHERE table_schema = $1
|
|
109
|
+
AND table_name = $2
|
|
87
110
|
AND column_name = $3
|
|
88
111
|
`, [schemaName, table, column]);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
112
|
+
if (!colCheck.rows || colCheck.rows.length === 0) {
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: `Column "${column}" not found in table ${qualifiedTable}. Verify the column name.`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const dataType = colCheck.rows[0]?.["data_type"];
|
|
119
|
+
const udtName = colCheck.rows[0]?.["udt_name"];
|
|
120
|
+
// Normalize type: use udt_name for user-defined types (like citext)
|
|
121
|
+
const currentType = dataType === "USER-DEFINED" ? udtName : dataType;
|
|
122
|
+
if (udtName === "citext") {
|
|
123
|
+
return {
|
|
124
|
+
success: true,
|
|
125
|
+
message: `Column ${column} is already citext`,
|
|
126
|
+
wasAlreadyCitext: true,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Validate that the column is a text-based type
|
|
130
|
+
const allowedTypes = [
|
|
131
|
+
"text",
|
|
132
|
+
"character varying",
|
|
133
|
+
"character",
|
|
134
|
+
"char",
|
|
135
|
+
"varchar",
|
|
136
|
+
];
|
|
137
|
+
const normalizedType = dataType.toLowerCase();
|
|
138
|
+
if (!allowedTypes.includes(normalizedType)) {
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
error: `Column "${column}" is type "${currentType}", not a text-based type`,
|
|
142
|
+
currentType,
|
|
143
|
+
allowedTypes: ["text", "varchar", "character varying"],
|
|
144
|
+
suggestion: `citext conversion only works for text-based columns. Column "${column}" is "${currentType}" which cannot be converted.`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// Check for dependent views before attempting the conversion
|
|
148
|
+
const depCheck = await adapter.executeQuery(`
|
|
149
|
+
SELECT DISTINCT
|
|
124
150
|
c.relname as dependent_view,
|
|
125
151
|
n.nspname as view_schema
|
|
126
152
|
FROM pg_depend d
|
|
@@ -135,40 +161,46 @@ Note: If views depend on this column, you must drop and recreate them manually b
|
|
|
135
161
|
AND t.relname = $2
|
|
136
162
|
AND a.attname = $3
|
|
137
163
|
`, [schemaName, table, column]);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
164
|
+
const dependentViews = depCheck.rows ?? [];
|
|
165
|
+
if (dependentViews.length > 0) {
|
|
166
|
+
return {
|
|
167
|
+
success: false,
|
|
168
|
+
error: "Column has dependent views that must be dropped before conversion",
|
|
169
|
+
dependentViews: dependentViews.map((v) => `${v["view_schema"]}.${v["dependent_view"]}`),
|
|
170
|
+
hint: "Drop the listed views, run this conversion, then recreate the views. PostgreSQL cannot ALTER COLUMN TYPE when views depend on it.",
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
await adapter.executeQuery(`
|
|
149
175
|
ALTER TABLE ${qualifiedTable}
|
|
150
176
|
ALTER COLUMN "${column}" TYPE citext USING "${column}"::citext
|
|
151
177
|
`);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
178
|
+
return {
|
|
179
|
+
success: true,
|
|
180
|
+
message: `Column ${column} converted from ${currentType} to citext`,
|
|
181
|
+
table: qualifiedTable,
|
|
182
|
+
previousType: currentType,
|
|
183
|
+
affectedViews: dependentViews.length > 0
|
|
184
|
+
? dependentViews.map((v) => `${v["view_schema"]}.${v["dependent_view"]}`)
|
|
185
|
+
: undefined,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
error: `Failed to convert column: ${errorMessage}`,
|
|
193
|
+
hint: "If views depend on this column, they may need to be dropped and recreated",
|
|
194
|
+
dependentViews: dependentViews.length > 0
|
|
195
|
+
? dependentViews.map((v) => `${v["view_schema"]}.${v["dependent_view"]}`)
|
|
196
|
+
: undefined,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
161
199
|
}
|
|
162
200
|
catch (error) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
error: `Failed to convert column: ${errorMessage}`,
|
|
167
|
-
hint: "If views depend on this column, they may need to be dropped and recreated",
|
|
168
|
-
dependentViews: dependentViews.length > 0
|
|
169
|
-
? dependentViews.map((v) => `${v["view_schema"]}.${v["dependent_view"]}`)
|
|
170
|
-
: undefined,
|
|
171
|
-
};
|
|
201
|
+
throw parsePostgresError(error, {
|
|
202
|
+
tool: "pg_citext_convert_column",
|
|
203
|
+
});
|
|
172
204
|
}
|
|
173
205
|
},
|
|
174
206
|
};
|
|
@@ -187,34 +219,46 @@ Useful for auditing case-insensitive columns.`,
|
|
|
187
219
|
annotations: readOnly("List Citext Columns"),
|
|
188
220
|
icons: getToolIcons("citext", readOnly("List Citext Columns")),
|
|
189
221
|
handler: async (params, _context) => {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
222
|
+
try {
|
|
223
|
+
const parsed = CitextListColumnsSchema.parse(params);
|
|
224
|
+
const { schema, limit: userLimit } = parsed;
|
|
225
|
+
// Validate schema existence when specified
|
|
226
|
+
if (schema !== undefined) {
|
|
227
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM information_schema.schemata
|
|
228
|
+
WHERE schema_name = $1`, [schema]);
|
|
229
|
+
if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
|
|
230
|
+
return {
|
|
231
|
+
success: false,
|
|
232
|
+
error: `Schema '${schema}' does not exist. Verify the schema name.`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Default limit of 100 to prevent large payloads
|
|
237
|
+
const DEFAULT_LIMIT = 100;
|
|
238
|
+
const effectiveLimit = userLimit === 0 ? undefined : (userLimit ?? DEFAULT_LIMIT);
|
|
239
|
+
const conditions = [
|
|
240
|
+
"udt_name = 'citext'",
|
|
241
|
+
"table_schema NOT IN ('pg_catalog', 'information_schema')",
|
|
242
|
+
];
|
|
243
|
+
const queryParams = [];
|
|
244
|
+
let paramIndex = 1;
|
|
245
|
+
if (schema !== undefined) {
|
|
246
|
+
conditions.push(`table_schema = $${String(paramIndex++)}`);
|
|
247
|
+
queryParams.push(schema);
|
|
248
|
+
}
|
|
249
|
+
const whereClause = conditions.join(" AND ");
|
|
250
|
+
// Count total columns first
|
|
251
|
+
const countSql = `
|
|
208
252
|
SELECT COUNT(*) as total
|
|
209
253
|
FROM information_schema.columns
|
|
210
254
|
WHERE ${whereClause}
|
|
211
255
|
`;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
SELECT
|
|
256
|
+
const countResult = await adapter.executeQuery(countSql, queryParams);
|
|
257
|
+
const totalCount = Number(countResult.rows?.[0]?.["total"] ?? 0);
|
|
258
|
+
// Add LIMIT clause
|
|
259
|
+
const limitClause = effectiveLimit !== undefined ? `LIMIT ${String(effectiveLimit)}` : "";
|
|
260
|
+
const sql = `
|
|
261
|
+
SELECT
|
|
218
262
|
table_schema,
|
|
219
263
|
table_name,
|
|
220
264
|
column_name,
|
|
@@ -225,18 +269,24 @@ Useful for auditing case-insensitive columns.`,
|
|
|
225
269
|
ORDER BY table_schema, table_name, ordinal_position
|
|
226
270
|
${limitClause}
|
|
227
271
|
`;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
272
|
+
const result = await adapter.executeQuery(sql, queryParams);
|
|
273
|
+
const columns = result.rows ?? [];
|
|
274
|
+
// Determine if results were truncated
|
|
275
|
+
const truncated = effectiveLimit !== undefined && columns.length < totalCount;
|
|
276
|
+
return {
|
|
277
|
+
columns,
|
|
278
|
+
count: columns.length,
|
|
279
|
+
totalCount,
|
|
280
|
+
truncated,
|
|
281
|
+
...(effectiveLimit !== undefined && { limit: effectiveLimit }),
|
|
282
|
+
...(schema !== undefined && { schema }),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
throw parsePostgresError(error, {
|
|
287
|
+
tool: "pg_citext_list_columns",
|
|
288
|
+
});
|
|
289
|
+
}
|
|
240
290
|
},
|
|
241
291
|
};
|
|
242
292
|
}
|
|
@@ -255,6 +305,29 @@ Looks for common patterns like email, username, name, slug, etc.`,
|
|
|
255
305
|
icons: getToolIcons("citext", readOnly("Analyze Citext Candidates")),
|
|
256
306
|
handler: async (params, _context) => {
|
|
257
307
|
const { patterns, schema, table, limit: userLimit, excludeSystemSchemas: userExcludeSystemSchemas, } = CitextAnalyzeCandidatesSchema.parse(params);
|
|
308
|
+
// Validate table/schema existence before querying
|
|
309
|
+
if (table !== undefined) {
|
|
310
|
+
const schemaName = schema ?? "public";
|
|
311
|
+
const qualifiedTable = `"${schemaName}"."${table}"`;
|
|
312
|
+
const tableCheck = await adapter.executeQuery(`SELECT 1 FROM information_schema.tables
|
|
313
|
+
WHERE table_schema = $1 AND table_name = $2`, [schemaName, table]);
|
|
314
|
+
if (!tableCheck.rows || tableCheck.rows.length === 0) {
|
|
315
|
+
return {
|
|
316
|
+
success: false,
|
|
317
|
+
error: `Table ${qualifiedTable} does not exist. Verify the table name and schema.`,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else if (schema !== undefined) {
|
|
322
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM information_schema.schemata
|
|
323
|
+
WHERE schema_name = $1`, [schema]);
|
|
324
|
+
if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
|
|
325
|
+
return {
|
|
326
|
+
success: false,
|
|
327
|
+
error: `Schema '${schema}' does not exist. Verify the schema name.`,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
258
331
|
// Default limit of 50 to prevent large payloads and transport truncation
|
|
259
332
|
const DEFAULT_LIMIT = 50;
|
|
260
333
|
const effectiveLimit = userLimit === 0 ? undefined : (userLimit ?? DEFAULT_LIMIT);
|
|
@@ -328,7 +401,7 @@ Looks for common patterns like email, username, name, slug, etc.`,
|
|
|
328
401
|
// Add LIMIT clause
|
|
329
402
|
const limitClause = effectiveLimit !== undefined ? `LIMIT ${String(effectiveLimit)}` : "";
|
|
330
403
|
const sql = `
|
|
331
|
-
SELECT
|
|
404
|
+
SELECT
|
|
332
405
|
table_schema,
|
|
333
406
|
table_name,
|
|
334
407
|
column_name,
|
|
@@ -402,50 +475,57 @@ Useful for testing citext behavior before converting columns.`,
|
|
|
402
475
|
annotations: readOnly("Compare Citext Values"),
|
|
403
476
|
icons: getToolIcons("citext", readOnly("Compare Citext Values")),
|
|
404
477
|
handler: async (params, _context) => {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
478
|
+
try {
|
|
479
|
+
// Use the schema for proper validation
|
|
480
|
+
const schema = z.object({
|
|
481
|
+
value1: z.string(),
|
|
482
|
+
value2: z.string(),
|
|
483
|
+
});
|
|
484
|
+
const { value1, value2 } = schema.parse(params);
|
|
485
|
+
const extCheck = await adapter.executeQuery(`
|
|
412
486
|
SELECT EXISTS(
|
|
413
487
|
SELECT 1 FROM pg_extension WHERE extname = 'citext'
|
|
414
488
|
) as installed
|
|
415
489
|
`);
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
SELECT
|
|
490
|
+
const hasExt = extCheck.rows?.[0]?.["installed"] ?? false;
|
|
491
|
+
if (hasExt) {
|
|
492
|
+
const result = await adapter.executeQuery(`
|
|
493
|
+
SELECT
|
|
420
494
|
$1::citext = $2::citext as citext_equal,
|
|
421
495
|
$1::text = $2::text as text_equal,
|
|
422
496
|
LOWER($1) = LOWER($2) as lower_equal
|
|
423
497
|
`, [value1, value2]);
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
SELECT
|
|
498
|
+
const row = result.rows?.[0];
|
|
499
|
+
return {
|
|
500
|
+
value1,
|
|
501
|
+
value2,
|
|
502
|
+
citextEqual: row?.["citext_equal"],
|
|
503
|
+
textEqual: row?.["text_equal"],
|
|
504
|
+
lowerEqual: row?.["lower_equal"],
|
|
505
|
+
extensionInstalled: true,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
const result = await adapter.executeQuery(`
|
|
510
|
+
SELECT
|
|
437
511
|
$1::text = $2::text as text_equal,
|
|
438
512
|
LOWER($1) = LOWER($2) as lower_equal
|
|
439
513
|
`, [value1, value2]);
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
514
|
+
const row = result.rows?.[0];
|
|
515
|
+
return {
|
|
516
|
+
value1,
|
|
517
|
+
value2,
|
|
518
|
+
textEqual: row?.["text_equal"],
|
|
519
|
+
lowerEqual: row?.["lower_equal"],
|
|
520
|
+
extensionInstalled: false,
|
|
521
|
+
hint: "Install citext extension for native case-insensitive comparisons",
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
throw parsePostgresError(error, {
|
|
527
|
+
tool: "pg_citext_compare",
|
|
528
|
+
});
|
|
449
529
|
}
|
|
450
530
|
},
|
|
451
531
|
};
|
|
@@ -465,111 +545,121 @@ Requires the 'table' parameter to specify which table to analyze.`,
|
|
|
465
545
|
annotations: readOnly("Citext Schema Advisor"),
|
|
466
546
|
icons: getToolIcons("citext", readOnly("Citext Schema Advisor")),
|
|
467
547
|
handler: async (params, _context) => {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
548
|
+
try {
|
|
549
|
+
const { table, schema } = CitextSchemaAdvisorSchema.parse(params);
|
|
550
|
+
const schemaName = schema ?? "public";
|
|
551
|
+
const qualifiedTable = `"${schemaName}"."${table}"`;
|
|
552
|
+
// First check if table exists
|
|
553
|
+
const tableCheck = await adapter.executeQuery(`
|
|
473
554
|
SELECT 1 FROM information_schema.tables
|
|
474
555
|
WHERE table_schema = $1 AND table_name = $2
|
|
475
556
|
`, [schemaName, table]);
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
557
|
+
if (!tableCheck.rows || tableCheck.rows.length === 0) {
|
|
558
|
+
return {
|
|
559
|
+
success: false,
|
|
560
|
+
error: `Table ${qualifiedTable} not found. Verify the table name and schema.`,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
const colResult = await adapter.executeQuery(`
|
|
564
|
+
SELECT
|
|
481
565
|
column_name,
|
|
482
566
|
data_type,
|
|
483
567
|
udt_name,
|
|
484
568
|
is_nullable,
|
|
485
569
|
character_maximum_length
|
|
486
570
|
FROM information_schema.columns
|
|
487
|
-
WHERE table_schema = $1
|
|
571
|
+
WHERE table_schema = $1
|
|
488
572
|
AND table_name = $2
|
|
489
573
|
AND data_type IN ('text', 'character varying', 'USER-DEFINED')
|
|
490
574
|
ORDER BY ordinal_position
|
|
491
575
|
`, [schemaName, table]);
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
576
|
+
const columns = colResult.rows ?? [];
|
|
577
|
+
const recommendations = [];
|
|
578
|
+
const highConfidencePatterns = [
|
|
579
|
+
"email",
|
|
580
|
+
"username",
|
|
581
|
+
"login",
|
|
582
|
+
"user_name",
|
|
583
|
+
];
|
|
584
|
+
const mediumConfidencePatterns = [
|
|
585
|
+
"name",
|
|
586
|
+
"slug",
|
|
587
|
+
"handle",
|
|
588
|
+
"code",
|
|
589
|
+
"sku",
|
|
590
|
+
"identifier",
|
|
591
|
+
"nickname",
|
|
592
|
+
];
|
|
593
|
+
for (const col of columns) {
|
|
594
|
+
const colName = col["column_name"].toLowerCase();
|
|
595
|
+
const dataType = col["data_type"];
|
|
596
|
+
const udtName = col["udt_name"];
|
|
597
|
+
if (udtName === "citext") {
|
|
598
|
+
recommendations.push({
|
|
599
|
+
column: col["column_name"],
|
|
600
|
+
currentType: "citext",
|
|
601
|
+
previousType: "text or varchar (converted)",
|
|
602
|
+
recommendation: "already_citext",
|
|
603
|
+
confidence: "high",
|
|
604
|
+
reason: "Column is already using citext",
|
|
605
|
+
});
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
const isHighConfidence = highConfidencePatterns.some((p) => colName.includes(p));
|
|
609
|
+
const isMediumConfidence = mediumConfidencePatterns.some((p) => colName.includes(p));
|
|
610
|
+
if (isHighConfidence) {
|
|
611
|
+
recommendations.push({
|
|
612
|
+
column: col["column_name"],
|
|
613
|
+
currentType: dataType,
|
|
614
|
+
recommendation: "convert",
|
|
615
|
+
confidence: "high",
|
|
616
|
+
reason: `Column name suggests case-insensitive data (${colName} matches common identifier patterns)`,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
else if (isMediumConfidence) {
|
|
620
|
+
recommendations.push({
|
|
621
|
+
column: col["column_name"],
|
|
622
|
+
currentType: dataType,
|
|
623
|
+
recommendation: "convert",
|
|
624
|
+
confidence: "medium",
|
|
625
|
+
reason: `Column name may benefit from case-insensitivity (${colName})`,
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
recommendations.push({
|
|
630
|
+
column: col["column_name"],
|
|
631
|
+
currentType: dataType,
|
|
632
|
+
recommendation: "keep",
|
|
633
|
+
confidence: "low",
|
|
634
|
+
reason: "No obvious case-insensitivity pattern detected",
|
|
635
|
+
});
|
|
636
|
+
}
|
|
552
637
|
}
|
|
638
|
+
const convertCount = recommendations.filter((r) => r.recommendation === "convert").length;
|
|
639
|
+
const highCount = recommendations.filter((r) => r.recommendation === "convert" && r.confidence === "high").length;
|
|
640
|
+
return {
|
|
641
|
+
table: `${schemaName}.${table}`,
|
|
642
|
+
recommendations,
|
|
643
|
+
summary: {
|
|
644
|
+
totalTextColumns: columns.length,
|
|
645
|
+
recommendConvert: convertCount,
|
|
646
|
+
highConfidence: highCount,
|
|
647
|
+
alreadyCitext: recommendations.filter((r) => r.recommendation === "already_citext").length,
|
|
648
|
+
},
|
|
649
|
+
nextSteps: convertCount > 0
|
|
650
|
+
? [
|
|
651
|
+
"Review recommendations above",
|
|
652
|
+
`Use pg_citext_convert_column to convert recommended columns`,
|
|
653
|
+
"Update application queries if they rely on case-sensitive comparisons",
|
|
654
|
+
]
|
|
655
|
+
: ["No columns require conversion"],
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
throw parsePostgresError(error, {
|
|
660
|
+
tool: "pg_citext_schema_advisor",
|
|
661
|
+
});
|
|
553
662
|
}
|
|
554
|
-
const convertCount = recommendations.filter((r) => r.recommendation === "convert").length;
|
|
555
|
-
const highCount = recommendations.filter((r) => r.recommendation === "convert" && r.confidence === "high").length;
|
|
556
|
-
return {
|
|
557
|
-
table: `${schemaName}.${table}`,
|
|
558
|
-
recommendations,
|
|
559
|
-
summary: {
|
|
560
|
-
totalTextColumns: columns.length,
|
|
561
|
-
recommendConvert: convertCount,
|
|
562
|
-
highConfidence: highCount,
|
|
563
|
-
alreadyCitext: recommendations.filter((r) => r.recommendation === "already_citext").length,
|
|
564
|
-
},
|
|
565
|
-
nextSteps: convertCount > 0
|
|
566
|
-
? [
|
|
567
|
-
"Review recommendations above",
|
|
568
|
-
`Use pg_citext_convert_column to convert recommended columns`,
|
|
569
|
-
"Update application queries if they rely on case-sensitive comparisons",
|
|
570
|
-
]
|
|
571
|
-
: ["No columns require conversion"],
|
|
572
|
-
};
|
|
573
663
|
},
|
|
574
664
|
};
|
|
575
665
|
}
|