@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.
Files changed (293) hide show
  1. package/README.md +202 -148
  2. package/dist/__tests__/benchmarks/codemode.bench.d.ts +10 -0
  3. package/dist/__tests__/benchmarks/codemode.bench.d.ts.map +1 -0
  4. package/dist/__tests__/benchmarks/codemode.bench.js +159 -0
  5. package/dist/__tests__/benchmarks/codemode.bench.js.map +1 -0
  6. package/dist/__tests__/benchmarks/connection-pool.bench.d.ts +10 -0
  7. package/dist/__tests__/benchmarks/connection-pool.bench.d.ts.map +1 -0
  8. package/dist/__tests__/benchmarks/connection-pool.bench.js +123 -0
  9. package/dist/__tests__/benchmarks/connection-pool.bench.js.map +1 -0
  10. package/dist/__tests__/benchmarks/handler-dispatch.bench.d.ts +11 -0
  11. package/dist/__tests__/benchmarks/handler-dispatch.bench.d.ts.map +1 -0
  12. package/dist/__tests__/benchmarks/handler-dispatch.bench.js +199 -0
  13. package/dist/__tests__/benchmarks/handler-dispatch.bench.js.map +1 -0
  14. package/dist/__tests__/benchmarks/logger-sanitization.bench.d.ts +15 -0
  15. package/dist/__tests__/benchmarks/logger-sanitization.bench.d.ts.map +1 -0
  16. package/dist/__tests__/benchmarks/logger-sanitization.bench.js +155 -0
  17. package/dist/__tests__/benchmarks/logger-sanitization.bench.js.map +1 -0
  18. package/dist/__tests__/benchmarks/resource-prompts.bench.d.ts +10 -0
  19. package/dist/__tests__/benchmarks/resource-prompts.bench.d.ts.map +1 -0
  20. package/dist/__tests__/benchmarks/resource-prompts.bench.js +181 -0
  21. package/dist/__tests__/benchmarks/resource-prompts.bench.js.map +1 -0
  22. package/dist/__tests__/benchmarks/schema-parsing.bench.d.ts +11 -0
  23. package/dist/__tests__/benchmarks/schema-parsing.bench.d.ts.map +1 -0
  24. package/dist/__tests__/benchmarks/schema-parsing.bench.js +209 -0
  25. package/dist/__tests__/benchmarks/schema-parsing.bench.js.map +1 -0
  26. package/dist/__tests__/benchmarks/tool-filtering.bench.d.ts +9 -0
  27. package/dist/__tests__/benchmarks/tool-filtering.bench.d.ts.map +1 -0
  28. package/dist/__tests__/benchmarks/tool-filtering.bench.js +83 -0
  29. package/dist/__tests__/benchmarks/tool-filtering.bench.js.map +1 -0
  30. package/dist/__tests__/benchmarks/transport-auth.bench.d.ts +10 -0
  31. package/dist/__tests__/benchmarks/transport-auth.bench.d.ts.map +1 -0
  32. package/dist/__tests__/benchmarks/transport-auth.bench.js +128 -0
  33. package/dist/__tests__/benchmarks/transport-auth.bench.js.map +1 -0
  34. package/dist/__tests__/benchmarks/utilities.bench.d.ts +10 -0
  35. package/dist/__tests__/benchmarks/utilities.bench.d.ts.map +1 -0
  36. package/dist/__tests__/benchmarks/utilities.bench.js +164 -0
  37. package/dist/__tests__/benchmarks/utilities.bench.js.map +1 -0
  38. package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
  39. package/dist/adapters/DatabaseAdapter.js +12 -0
  40. package/dist/adapters/DatabaseAdapter.js.map +1 -1
  41. package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
  42. package/dist/adapters/postgresql/PostgresAdapter.js +56 -3
  43. package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
  44. package/dist/adapters/postgresql/prompts/ltree.js +2 -2
  45. package/dist/adapters/postgresql/prompts/ltree.js.map +1 -1
  46. package/dist/adapters/postgresql/schemas/admin.d.ts +10 -5
  47. package/dist/adapters/postgresql/schemas/admin.d.ts.map +1 -1
  48. package/dist/adapters/postgresql/schemas/admin.js +10 -5
  49. package/dist/adapters/postgresql/schemas/admin.js.map +1 -1
  50. package/dist/adapters/postgresql/schemas/backup.d.ts +45 -27
  51. package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -1
  52. package/dist/adapters/postgresql/schemas/backup.js +64 -26
  53. package/dist/adapters/postgresql/schemas/backup.js.map +1 -1
  54. package/dist/adapters/postgresql/schemas/core.d.ts +53 -19
  55. package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -1
  56. package/dist/adapters/postgresql/schemas/core.js +61 -17
  57. package/dist/adapters/postgresql/schemas/core.js.map +1 -1
  58. package/dist/adapters/postgresql/schemas/cron.d.ts +51 -32
  59. package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -1
  60. package/dist/adapters/postgresql/schemas/cron.js +64 -44
  61. package/dist/adapters/postgresql/schemas/cron.js.map +1 -1
  62. package/dist/adapters/postgresql/schemas/extensions.d.ts +224 -110
  63. package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -1
  64. package/dist/adapters/postgresql/schemas/extensions.js +245 -96
  65. package/dist/adapters/postgresql/schemas/extensions.js.map +1 -1
  66. package/dist/adapters/postgresql/schemas/index.d.ts +7 -6
  67. package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -1
  68. package/dist/adapters/postgresql/schemas/index.js +16 -8
  69. package/dist/adapters/postgresql/schemas/index.js.map +1 -1
  70. package/dist/adapters/postgresql/schemas/introspection.d.ts +445 -0
  71. package/dist/adapters/postgresql/schemas/introspection.d.ts.map +1 -0
  72. package/dist/adapters/postgresql/schemas/introspection.js +478 -0
  73. package/dist/adapters/postgresql/schemas/introspection.js.map +1 -0
  74. package/dist/adapters/postgresql/schemas/jsonb.d.ts +102 -42
  75. package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +1 -1
  76. package/dist/adapters/postgresql/schemas/jsonb.js +125 -30
  77. package/dist/adapters/postgresql/schemas/jsonb.js.map +1 -1
  78. package/dist/adapters/postgresql/schemas/monitoring.d.ts +69 -36
  79. package/dist/adapters/postgresql/schemas/monitoring.d.ts.map +1 -1
  80. package/dist/adapters/postgresql/schemas/monitoring.js +98 -40
  81. package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -1
  82. package/dist/adapters/postgresql/schemas/partitioning.d.ts +21 -24
  83. package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
  84. package/dist/adapters/postgresql/schemas/partitioning.js +26 -14
  85. package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
  86. package/dist/adapters/postgresql/schemas/partman.d.ts +69 -0
  87. package/dist/adapters/postgresql/schemas/partman.d.ts.map +1 -1
  88. package/dist/adapters/postgresql/schemas/partman.js +46 -33
  89. package/dist/adapters/postgresql/schemas/partman.js.map +1 -1
  90. package/dist/adapters/postgresql/schemas/performance.d.ts +97 -49
  91. package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -1
  92. package/dist/adapters/postgresql/schemas/performance.js +139 -34
  93. package/dist/adapters/postgresql/schemas/performance.js.map +1 -1
  94. package/dist/adapters/postgresql/schemas/postgis.d.ts +20 -0
  95. package/dist/adapters/postgresql/schemas/postgis.d.ts.map +1 -1
  96. package/dist/adapters/postgresql/schemas/postgis.js +40 -0
  97. package/dist/adapters/postgresql/schemas/postgis.js.map +1 -1
  98. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +50 -30
  99. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -1
  100. package/dist/adapters/postgresql/schemas/schema-mgmt.js +105 -33
  101. package/dist/adapters/postgresql/schemas/schema-mgmt.js.map +1 -1
  102. package/dist/adapters/postgresql/schemas/stats.d.ts +33 -20
  103. package/dist/adapters/postgresql/schemas/stats.d.ts.map +1 -1
  104. package/dist/adapters/postgresql/schemas/stats.js +36 -20
  105. package/dist/adapters/postgresql/schemas/stats.js.map +1 -1
  106. package/dist/adapters/postgresql/schemas/text-search.d.ts +34 -19
  107. package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -1
  108. package/dist/adapters/postgresql/schemas/text-search.js +52 -13
  109. package/dist/adapters/postgresql/schemas/text-search.js.map +1 -1
  110. package/dist/adapters/postgresql/tools/admin.d.ts.map +1 -1
  111. package/dist/adapters/postgresql/tools/admin.js +272 -186
  112. package/dist/adapters/postgresql/tools/admin.js.map +1 -1
  113. package/dist/adapters/postgresql/tools/backup/dump.d.ts.map +1 -1
  114. package/dist/adapters/postgresql/tools/backup/dump.js +376 -350
  115. package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
  116. package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -1
  117. package/dist/adapters/postgresql/tools/citext.js +333 -243
  118. package/dist/adapters/postgresql/tools/citext.js.map +1 -1
  119. package/dist/adapters/postgresql/tools/codemode/index.d.ts.map +1 -1
  120. package/dist/adapters/postgresql/tools/codemode/index.js +2 -11
  121. package/dist/adapters/postgresql/tools/codemode/index.js.map +1 -1
  122. package/dist/adapters/postgresql/tools/core/convenience.d.ts +9 -1
  123. package/dist/adapters/postgresql/tools/core/convenience.d.ts.map +1 -1
  124. package/dist/adapters/postgresql/tools/core/convenience.js +101 -19
  125. package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -1
  126. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts +48 -0
  127. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts.map +1 -0
  128. package/dist/adapters/postgresql/tools/core/error-helpers.js +256 -0
  129. package/dist/adapters/postgresql/tools/core/error-helpers.js.map +1 -0
  130. package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -1
  131. package/dist/adapters/postgresql/tools/core/health.js +18 -4
  132. package/dist/adapters/postgresql/tools/core/health.js.map +1 -1
  133. package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -1
  134. package/dist/adapters/postgresql/tools/core/indexes.js +48 -6
  135. package/dist/adapters/postgresql/tools/core/indexes.js.map +1 -1
  136. package/dist/adapters/postgresql/tools/core/objects.d.ts.map +1 -1
  137. package/dist/adapters/postgresql/tools/core/objects.js +104 -85
  138. package/dist/adapters/postgresql/tools/core/objects.js.map +1 -1
  139. package/dist/adapters/postgresql/tools/core/query.d.ts.map +1 -1
  140. package/dist/adapters/postgresql/tools/core/query.js +100 -42
  141. package/dist/adapters/postgresql/tools/core/query.js.map +1 -1
  142. package/dist/adapters/postgresql/tools/core/schemas.d.ts +51 -25
  143. package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
  144. package/dist/adapters/postgresql/tools/core/schemas.js +51 -25
  145. package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -1
  146. package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -1
  147. package/dist/adapters/postgresql/tools/core/tables.js +72 -32
  148. package/dist/adapters/postgresql/tools/core/tables.js.map +1 -1
  149. package/dist/adapters/postgresql/tools/cron.d.ts.map +1 -1
  150. package/dist/adapters/postgresql/tools/cron.js +333 -206
  151. package/dist/adapters/postgresql/tools/cron.js.map +1 -1
  152. package/dist/adapters/postgresql/tools/introspection.d.ts +15 -0
  153. package/dist/adapters/postgresql/tools/introspection.d.ts.map +1 -0
  154. package/dist/adapters/postgresql/tools/introspection.js +1682 -0
  155. package/dist/adapters/postgresql/tools/introspection.js.map +1 -0
  156. package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +1 -1
  157. package/dist/adapters/postgresql/tools/jsonb/advanced.js +394 -297
  158. package/dist/adapters/postgresql/tools/jsonb/advanced.js.map +1 -1
  159. package/dist/adapters/postgresql/tools/jsonb/basic.d.ts.map +1 -1
  160. package/dist/adapters/postgresql/tools/jsonb/basic.js +686 -398
  161. package/dist/adapters/postgresql/tools/jsonb/basic.js.map +1 -1
  162. package/dist/adapters/postgresql/tools/kcache.d.ts.map +1 -1
  163. package/dist/adapters/postgresql/tools/kcache.js +278 -246
  164. package/dist/adapters/postgresql/tools/kcache.js.map +1 -1
  165. package/dist/adapters/postgresql/tools/ltree.d.ts.map +1 -1
  166. package/dist/adapters/postgresql/tools/ltree.js +137 -38
  167. package/dist/adapters/postgresql/tools/ltree.js.map +1 -1
  168. package/dist/adapters/postgresql/tools/monitoring.d.ts.map +1 -1
  169. package/dist/adapters/postgresql/tools/monitoring.js +86 -55
  170. package/dist/adapters/postgresql/tools/monitoring.js.map +1 -1
  171. package/dist/adapters/postgresql/tools/partitioning.d.ts.map +1 -1
  172. package/dist/adapters/postgresql/tools/partitioning.js +79 -15
  173. package/dist/adapters/postgresql/tools/partitioning.js.map +1 -1
  174. package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -1
  175. package/dist/adapters/postgresql/tools/partman/management.js +43 -56
  176. package/dist/adapters/postgresql/tools/partman/management.js.map +1 -1
  177. package/dist/adapters/postgresql/tools/partman/operations.d.ts.map +1 -1
  178. package/dist/adapters/postgresql/tools/partman/operations.js +137 -24
  179. package/dist/adapters/postgresql/tools/partman/operations.js.map +1 -1
  180. package/dist/adapters/postgresql/tools/performance/analysis.d.ts.map +1 -1
  181. package/dist/adapters/postgresql/tools/performance/analysis.js +276 -165
  182. package/dist/adapters/postgresql/tools/performance/analysis.js.map +1 -1
  183. package/dist/adapters/postgresql/tools/performance/explain.d.ts.map +1 -1
  184. package/dist/adapters/postgresql/tools/performance/explain.js +61 -21
  185. package/dist/adapters/postgresql/tools/performance/explain.js.map +1 -1
  186. package/dist/adapters/postgresql/tools/performance/monitoring.d.ts.map +1 -1
  187. package/dist/adapters/postgresql/tools/performance/monitoring.js +52 -12
  188. package/dist/adapters/postgresql/tools/performance/monitoring.js.map +1 -1
  189. package/dist/adapters/postgresql/tools/performance/optimization.d.ts.map +1 -1
  190. package/dist/adapters/postgresql/tools/performance/optimization.js +92 -81
  191. package/dist/adapters/postgresql/tools/performance/optimization.js.map +1 -1
  192. package/dist/adapters/postgresql/tools/performance/stats.d.ts.map +1 -1
  193. package/dist/adapters/postgresql/tools/performance/stats.js +182 -60
  194. package/dist/adapters/postgresql/tools/performance/stats.js.map +1 -1
  195. package/dist/adapters/postgresql/tools/pgcrypto.d.ts.map +1 -1
  196. package/dist/adapters/postgresql/tools/pgcrypto.js +277 -102
  197. package/dist/adapters/postgresql/tools/pgcrypto.js.map +1 -1
  198. package/dist/adapters/postgresql/tools/postgis/advanced.d.ts.map +1 -1
  199. package/dist/adapters/postgresql/tools/postgis/advanced.js +298 -230
  200. package/dist/adapters/postgresql/tools/postgis/advanced.js.map +1 -1
  201. package/dist/adapters/postgresql/tools/postgis/basic.d.ts.map +1 -1
  202. package/dist/adapters/postgresql/tools/postgis/basic.js +370 -251
  203. package/dist/adapters/postgresql/tools/postgis/basic.js.map +1 -1
  204. package/dist/adapters/postgresql/tools/postgis/standalone.d.ts.map +1 -1
  205. package/dist/adapters/postgresql/tools/postgis/standalone.js +135 -51
  206. package/dist/adapters/postgresql/tools/postgis/standalone.js.map +1 -1
  207. package/dist/adapters/postgresql/tools/schema.d.ts.map +1 -1
  208. package/dist/adapters/postgresql/tools/schema.js +580 -233
  209. package/dist/adapters/postgresql/tools/schema.js.map +1 -1
  210. package/dist/adapters/postgresql/tools/stats/advanced.d.ts.map +1 -1
  211. package/dist/adapters/postgresql/tools/stats/advanced.js +567 -506
  212. package/dist/adapters/postgresql/tools/stats/advanced.js.map +1 -1
  213. package/dist/adapters/postgresql/tools/stats/basic.d.ts.map +1 -1
  214. package/dist/adapters/postgresql/tools/stats/basic.js +340 -316
  215. package/dist/adapters/postgresql/tools/stats/basic.js.map +1 -1
  216. package/dist/adapters/postgresql/tools/text.d.ts.map +1 -1
  217. package/dist/adapters/postgresql/tools/text.js +690 -337
  218. package/dist/adapters/postgresql/tools/text.js.map +1 -1
  219. package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -1
  220. package/dist/adapters/postgresql/tools/transactions.js +157 -50
  221. package/dist/adapters/postgresql/tools/transactions.js.map +1 -1
  222. package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +1 -1
  223. package/dist/adapters/postgresql/tools/vector/advanced.js +18 -0
  224. package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -1
  225. package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -1
  226. package/dist/adapters/postgresql/tools/vector/basic.js +100 -53
  227. package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -1
  228. package/dist/auth/auth-context.d.ts +28 -0
  229. package/dist/auth/auth-context.d.ts.map +1 -0
  230. package/dist/auth/auth-context.js +37 -0
  231. package/dist/auth/auth-context.js.map +1 -0
  232. package/dist/auth/scope-map.d.ts +20 -0
  233. package/dist/auth/scope-map.d.ts.map +1 -0
  234. package/dist/auth/scope-map.js +40 -0
  235. package/dist/auth/scope-map.js.map +1 -0
  236. package/dist/auth/scopes.d.ts.map +1 -1
  237. package/dist/auth/scopes.js +2 -0
  238. package/dist/auth/scopes.js.map +1 -1
  239. package/dist/cli.js +1 -1
  240. package/dist/cli.js.map +1 -1
  241. package/dist/codemode/api.d.ts +1 -0
  242. package/dist/codemode/api.d.ts.map +1 -1
  243. package/dist/codemode/api.js +35 -1
  244. package/dist/codemode/api.js.map +1 -1
  245. package/dist/codemode/index.d.ts +0 -2
  246. package/dist/codemode/index.d.ts.map +1 -1
  247. package/dist/codemode/index.js +0 -4
  248. package/dist/codemode/index.js.map +1 -1
  249. package/dist/codemode/sandbox.d.ts +14 -1
  250. package/dist/codemode/sandbox.d.ts.map +1 -1
  251. package/dist/codemode/sandbox.js +58 -19
  252. package/dist/codemode/sandbox.js.map +1 -1
  253. package/dist/codemode/types.d.ts.map +1 -1
  254. package/dist/codemode/types.js +3 -0
  255. package/dist/codemode/types.js.map +1 -1
  256. package/dist/constants/ServerInstructions.d.ts +5 -1
  257. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  258. package/dist/constants/ServerInstructions.js +117 -31
  259. package/dist/constants/ServerInstructions.js.map +1 -1
  260. package/dist/filtering/ToolConstants.d.ts +22 -19
  261. package/dist/filtering/ToolConstants.d.ts.map +1 -1
  262. package/dist/filtering/ToolConstants.js +48 -37
  263. package/dist/filtering/ToolConstants.js.map +1 -1
  264. package/dist/filtering/ToolFilter.d.ts.map +1 -1
  265. package/dist/filtering/ToolFilter.js +10 -13
  266. package/dist/filtering/ToolFilter.js.map +1 -1
  267. package/dist/pool/ConnectionPool.js +1 -1
  268. package/dist/pool/ConnectionPool.js.map +1 -1
  269. package/dist/transports/http.d.ts +1 -0
  270. package/dist/transports/http.d.ts.map +1 -1
  271. package/dist/transports/http.js +75 -21
  272. package/dist/transports/http.js.map +1 -1
  273. package/dist/types/filtering.d.ts +2 -2
  274. package/dist/types/filtering.d.ts.map +1 -1
  275. package/dist/utils/icons.d.ts.map +1 -1
  276. package/dist/utils/icons.js +5 -0
  277. package/dist/utils/icons.js.map +1 -1
  278. package/dist/utils/where-clause.d.ts.map +1 -1
  279. package/dist/utils/where-clause.js +24 -0
  280. package/dist/utils/where-clause.js.map +1 -1
  281. package/package.json +20 -13
  282. package/dist/codemode/sandbox-factory.d.ts +0 -72
  283. package/dist/codemode/sandbox-factory.d.ts.map +0 -1
  284. package/dist/codemode/sandbox-factory.js +0 -88
  285. package/dist/codemode/sandbox-factory.js.map +0 -1
  286. package/dist/codemode/worker-sandbox.d.ts +0 -82
  287. package/dist/codemode/worker-sandbox.d.ts.map +0 -1
  288. package/dist/codemode/worker-sandbox.js +0 -244
  289. package/dist/codemode/worker-sandbox.js.map +0 -1
  290. package/dist/codemode/worker-script.d.ts +0 -8
  291. package/dist/codemode/worker-script.d.ts.map +0 -1
  292. package/dist/codemode/worker-script.js +0 -113
  293. 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
- await adapter.executeQuery("CREATE EXTENSION IF NOT EXISTS citext");
46
- return {
47
- success: true,
48
- message: "citext extension enabled",
49
- usage: "Create columns with type CITEXT instead of TEXT for case-insensitive comparisons",
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
- const parsed = CitextConvertColumnSchema.parse(params ?? {});
70
- const { table, column, schema: schemaOpt } = parsed;
71
- const schemaName = schemaOpt ?? "public";
72
- const qualifiedTable = `"${schemaName}"."${table}"`;
73
- const extCheck = await adapter.executeQuery(`
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
- const hasExt = extCheck.rows?.[0]?.["installed"] ?? false;
79
- if (!hasExt) {
80
- throw new Error("citext extension is not installed. Run pg_citext_create_extension first.");
81
- }
82
- const colCheck = await adapter.executeQuery(`
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
- if (!colCheck.rows || colCheck.rows.length === 0) {
90
- throw new Error(`Column "${column}" not found in table ${qualifiedTable}. Verify the table and column names.`);
91
- }
92
- const dataType = colCheck.rows[0]?.["data_type"];
93
- const udtName = colCheck.rows[0]?.["udt_name"];
94
- // Normalize type: use udt_name for user-defined types (like citext)
95
- const currentType = dataType === "USER-DEFINED" ? udtName : dataType;
96
- if (udtName === "citext") {
97
- return {
98
- success: true,
99
- message: `Column ${column} is already citext`,
100
- wasAlreadyCitext: true,
101
- };
102
- }
103
- // Validate that the column is a text-based type
104
- const allowedTypes = [
105
- "text",
106
- "character varying",
107
- "character",
108
- "char",
109
- "varchar",
110
- ];
111
- const normalizedType = dataType.toLowerCase();
112
- if (!allowedTypes.includes(normalizedType)) {
113
- return {
114
- success: false,
115
- error: `Column "${column}" is type "${currentType}", not a text-based type`,
116
- currentType,
117
- allowedTypes: ["text", "varchar", "character varying"],
118
- suggestion: `citext conversion only works for text-based columns. Column "${column}" is "${currentType}" which cannot be converted.`,
119
- };
120
- }
121
- // Check for dependent views before attempting the conversion
122
- const depCheck = await adapter.executeQuery(`
123
- SELECT DISTINCT
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
- const dependentViews = depCheck.rows ?? [];
139
- if (dependentViews.length > 0) {
140
- return {
141
- success: false,
142
- error: "Column has dependent views that must be dropped before conversion",
143
- dependentViews: dependentViews.map((v) => `${v["view_schema"]}.${v["dependent_view"]}`),
144
- hint: "Drop the listed views, run this conversion, then recreate the views. PostgreSQL cannot ALTER COLUMN TYPE when views depend on it.",
145
- };
146
- }
147
- try {
148
- await adapter.executeQuery(`
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
- return {
153
- success: true,
154
- message: `Column ${column} converted from ${currentType} to citext`,
155
- table: qualifiedTable,
156
- previousType: currentType,
157
- affectedViews: dependentViews.length > 0
158
- ? dependentViews.map((v) => `${v["view_schema"]}.${v["dependent_view"]}`)
159
- : undefined,
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
- const errorMessage = error instanceof Error ? error.message : String(error);
164
- return {
165
- success: false,
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
- const parsed = CitextListColumnsSchema.parse(params);
191
- const { schema, limit: userLimit } = parsed;
192
- // Default limit of 100 to prevent large payloads
193
- const DEFAULT_LIMIT = 100;
194
- const effectiveLimit = userLimit === 0 ? undefined : (userLimit ?? DEFAULT_LIMIT);
195
- const conditions = [
196
- "udt_name = 'citext'",
197
- "table_schema NOT IN ('pg_catalog', 'information_schema')",
198
- ];
199
- const queryParams = [];
200
- let paramIndex = 1;
201
- if (schema !== undefined) {
202
- conditions.push(`table_schema = $${String(paramIndex++)}`);
203
- queryParams.push(schema);
204
- }
205
- const whereClause = conditions.join(" AND ");
206
- // Count total columns first
207
- const countSql = `
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
- const countResult = await adapter.executeQuery(countSql, queryParams);
213
- const totalCount = Number(countResult.rows?.[0]?.["total"] ?? 0);
214
- // Add LIMIT clause
215
- const limitClause = effectiveLimit !== undefined ? `LIMIT ${String(effectiveLimit)}` : "";
216
- const sql = `
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
- const result = await adapter.executeQuery(sql, queryParams);
229
- const columns = result.rows ?? [];
230
- // Determine if results were truncated
231
- const truncated = effectiveLimit !== undefined && columns.length < totalCount;
232
- return {
233
- columns,
234
- count: columns.length,
235
- totalCount,
236
- truncated,
237
- ...(effectiveLimit !== undefined && { limit: effectiveLimit }),
238
- ...(schema !== undefined && { schema }),
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
- // Use the schema for proper validation
406
- const schema = z.object({
407
- value1: z.string(),
408
- value2: z.string(),
409
- });
410
- const { value1, value2 } = schema.parse(params);
411
- const extCheck = await adapter.executeQuery(`
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
- const hasExt = extCheck.rows?.[0]?.["installed"] ?? false;
417
- if (hasExt) {
418
- const result = await adapter.executeQuery(`
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
- const row = result.rows?.[0];
425
- return {
426
- value1,
427
- value2,
428
- citextEqual: row?.["citext_equal"],
429
- textEqual: row?.["text_equal"],
430
- lowerEqual: row?.["lower_equal"],
431
- extensionInstalled: true,
432
- };
433
- }
434
- else {
435
- const result = await adapter.executeQuery(`
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
- const row = result.rows?.[0];
441
- return {
442
- value1,
443
- value2,
444
- textEqual: row?.["text_equal"],
445
- lowerEqual: row?.["lower_equal"],
446
- extensionInstalled: false,
447
- hint: "Install citext extension for native case-insensitive comparisons",
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
- const { table, schema } = CitextSchemaAdvisorSchema.parse(params);
469
- const schemaName = schema ?? "public";
470
- const qualifiedTable = `"${schemaName}"."${table}"`;
471
- // First check if table exists
472
- const tableCheck = await adapter.executeQuery(`
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
- if (!tableCheck.rows || tableCheck.rows.length === 0) {
477
- throw new Error(`Table ${qualifiedTable} not found. Verify the table name and schema.`);
478
- }
479
- const colResult = await adapter.executeQuery(`
480
- SELECT
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
- const columns = colResult.rows ?? [];
493
- const recommendations = [];
494
- const highConfidencePatterns = [
495
- "email",
496
- "username",
497
- "login",
498
- "user_name",
499
- ];
500
- const mediumConfidencePatterns = [
501
- "name",
502
- "slug",
503
- "handle",
504
- "code",
505
- "sku",
506
- "identifier",
507
- "nickname",
508
- ];
509
- for (const col of columns) {
510
- const colName = col["column_name"].toLowerCase();
511
- const dataType = col["data_type"];
512
- const udtName = col["udt_name"];
513
- if (udtName === "citext") {
514
- recommendations.push({
515
- column: col["column_name"],
516
- currentType: "citext",
517
- previousType: "text or varchar (converted)",
518
- recommendation: "already_citext",
519
- confidence: "high",
520
- reason: "Column is already using citext",
521
- });
522
- continue;
523
- }
524
- const isHighConfidence = highConfidencePatterns.some((p) => colName.includes(p));
525
- const isMediumConfidence = mediumConfidencePatterns.some((p) => colName.includes(p));
526
- if (isHighConfidence) {
527
- recommendations.push({
528
- column: col["column_name"],
529
- currentType: dataType,
530
- recommendation: "convert",
531
- confidence: "high",
532
- reason: `Column name suggests case-insensitive data (${colName} matches common identifier patterns)`,
533
- });
534
- }
535
- else if (isMediumConfidence) {
536
- recommendations.push({
537
- column: col["column_name"],
538
- currentType: dataType,
539
- recommendation: "convert",
540
- confidence: "medium",
541
- reason: `Column name may benefit from case-insensitivity (${colName})`,
542
- });
543
- }
544
- else {
545
- recommendations.push({
546
- column: col["column_name"],
547
- currentType: dataType,
548
- recommendation: "keep",
549
- confidence: "low",
550
- reason: "No obvious case-insensitivity pattern detected",
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
  }