@neverinfamous/postgres-mcp 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/README.md +95 -81
  2. package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
  3. package/dist/__tests__/mocks/adapter.js +0 -1
  4. package/dist/__tests__/mocks/adapter.js.map +1 -1
  5. package/dist/__tests__/mocks/pool.d.ts.map +1 -1
  6. package/dist/__tests__/mocks/pool.js +0 -1
  7. package/dist/__tests__/mocks/pool.js.map +1 -1
  8. package/dist/adapters/DatabaseAdapter.js +1 -1
  9. package/dist/adapters/DatabaseAdapter.js.map +1 -1
  10. package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
  11. package/dist/adapters/postgresql/PostgresAdapter.js +78 -8
  12. package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
  13. package/dist/adapters/postgresql/prompts/backup.d.ts.map +1 -1
  14. package/dist/adapters/postgresql/prompts/backup.js +2 -3
  15. package/dist/adapters/postgresql/prompts/backup.js.map +1 -1
  16. package/dist/adapters/postgresql/prompts/citext.d.ts.map +1 -1
  17. package/dist/adapters/postgresql/prompts/citext.js +3 -4
  18. package/dist/adapters/postgresql/prompts/citext.js.map +1 -1
  19. package/dist/adapters/postgresql/prompts/extensionSetup.d.ts.map +1 -1
  20. package/dist/adapters/postgresql/prompts/extensionSetup.js +2 -3
  21. package/dist/adapters/postgresql/prompts/extensionSetup.js.map +1 -1
  22. package/dist/adapters/postgresql/prompts/health.d.ts.map +1 -1
  23. package/dist/adapters/postgresql/prompts/health.js +2 -3
  24. package/dist/adapters/postgresql/prompts/health.js.map +1 -1
  25. package/dist/adapters/postgresql/prompts/index.js +20 -27
  26. package/dist/adapters/postgresql/prompts/index.js.map +1 -1
  27. package/dist/adapters/postgresql/prompts/indexTuning.d.ts.map +1 -1
  28. package/dist/adapters/postgresql/prompts/indexTuning.js +2 -3
  29. package/dist/adapters/postgresql/prompts/indexTuning.js.map +1 -1
  30. package/dist/adapters/postgresql/prompts/kcache.d.ts.map +1 -1
  31. package/dist/adapters/postgresql/prompts/kcache.js +3 -4
  32. package/dist/adapters/postgresql/prompts/kcache.js.map +1 -1
  33. package/dist/adapters/postgresql/prompts/ltree.d.ts.map +1 -1
  34. package/dist/adapters/postgresql/prompts/ltree.js +5 -6
  35. package/dist/adapters/postgresql/prompts/ltree.js.map +1 -1
  36. package/dist/adapters/postgresql/prompts/partman.d.ts.map +1 -1
  37. package/dist/adapters/postgresql/prompts/partman.js +2 -3
  38. package/dist/adapters/postgresql/prompts/partman.js.map +1 -1
  39. package/dist/adapters/postgresql/prompts/pgcron.d.ts.map +1 -1
  40. package/dist/adapters/postgresql/prompts/pgcron.js +2 -3
  41. package/dist/adapters/postgresql/prompts/pgcron.js.map +1 -1
  42. package/dist/adapters/postgresql/prompts/pgcrypto.d.ts.map +1 -1
  43. package/dist/adapters/postgresql/prompts/pgcrypto.js +3 -4
  44. package/dist/adapters/postgresql/prompts/pgcrypto.js.map +1 -1
  45. package/dist/adapters/postgresql/prompts/pgvector.d.ts.map +1 -1
  46. package/dist/adapters/postgresql/prompts/pgvector.js +3 -4
  47. package/dist/adapters/postgresql/prompts/pgvector.js.map +1 -1
  48. package/dist/adapters/postgresql/prompts/postgis.d.ts.map +1 -1
  49. package/dist/adapters/postgresql/prompts/postgis.js +2 -3
  50. package/dist/adapters/postgresql/prompts/postgis.js.map +1 -1
  51. package/dist/adapters/postgresql/schemas/admin.d.ts +10 -5
  52. package/dist/adapters/postgresql/schemas/admin.d.ts.map +1 -1
  53. package/dist/adapters/postgresql/schemas/admin.js +10 -5
  54. package/dist/adapters/postgresql/schemas/admin.js.map +1 -1
  55. package/dist/adapters/postgresql/schemas/backup.d.ts +8 -4
  56. package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -1
  57. package/dist/adapters/postgresql/schemas/backup.js +11 -4
  58. package/dist/adapters/postgresql/schemas/backup.js.map +1 -1
  59. package/dist/adapters/postgresql/schemas/core.d.ts +54 -19
  60. package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -1
  61. package/dist/adapters/postgresql/schemas/core.js +65 -17
  62. package/dist/adapters/postgresql/schemas/core.js.map +1 -1
  63. package/dist/adapters/postgresql/schemas/cron.d.ts +51 -32
  64. package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -1
  65. package/dist/adapters/postgresql/schemas/cron.js +64 -44
  66. package/dist/adapters/postgresql/schemas/cron.js.map +1 -1
  67. package/dist/adapters/postgresql/schemas/extensions.d.ts +168 -73
  68. package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -1
  69. package/dist/adapters/postgresql/schemas/extensions.js +179 -62
  70. package/dist/adapters/postgresql/schemas/extensions.js.map +1 -1
  71. package/dist/adapters/postgresql/schemas/index.d.ts +5 -5
  72. package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -1
  73. package/dist/adapters/postgresql/schemas/index.js +9 -7
  74. package/dist/adapters/postgresql/schemas/index.js.map +1 -1
  75. package/dist/adapters/postgresql/schemas/jsonb.d.ts +94 -42
  76. package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +1 -1
  77. package/dist/adapters/postgresql/schemas/jsonb.js +101 -30
  78. package/dist/adapters/postgresql/schemas/jsonb.js.map +1 -1
  79. package/dist/adapters/postgresql/schemas/monitoring.d.ts +28 -11
  80. package/dist/adapters/postgresql/schemas/monitoring.d.ts.map +1 -1
  81. package/dist/adapters/postgresql/schemas/monitoring.js +49 -24
  82. package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -1
  83. package/dist/adapters/postgresql/schemas/partitioning.d.ts +15 -11
  84. package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
  85. package/dist/adapters/postgresql/schemas/partitioning.js +17 -13
  86. package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
  87. package/dist/adapters/postgresql/schemas/performance.d.ts +62 -31
  88. package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -1
  89. package/dist/adapters/postgresql/schemas/performance.js +86 -24
  90. package/dist/adapters/postgresql/schemas/performance.js.map +1 -1
  91. package/dist/adapters/postgresql/schemas/postgis.d.ts +20 -0
  92. package/dist/adapters/postgresql/schemas/postgis.d.ts.map +1 -1
  93. package/dist/adapters/postgresql/schemas/postgis.js +20 -3
  94. package/dist/adapters/postgresql/schemas/postgis.js.map +1 -1
  95. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +35 -23
  96. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -1
  97. package/dist/adapters/postgresql/schemas/schema-mgmt.js +69 -26
  98. package/dist/adapters/postgresql/schemas/schema-mgmt.js.map +1 -1
  99. package/dist/adapters/postgresql/schemas/stats.d.ts +33 -20
  100. package/dist/adapters/postgresql/schemas/stats.d.ts.map +1 -1
  101. package/dist/adapters/postgresql/schemas/stats.js +36 -20
  102. package/dist/adapters/postgresql/schemas/stats.js.map +1 -1
  103. package/dist/adapters/postgresql/schemas/text-search.d.ts +8 -5
  104. package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -1
  105. package/dist/adapters/postgresql/schemas/text-search.js +15 -5
  106. package/dist/adapters/postgresql/schemas/text-search.js.map +1 -1
  107. package/dist/adapters/postgresql/tools/admin.d.ts.map +1 -1
  108. package/dist/adapters/postgresql/tools/admin.js +211 -140
  109. package/dist/adapters/postgresql/tools/admin.js.map +1 -1
  110. package/dist/adapters/postgresql/tools/backup/dump.d.ts.map +1 -1
  111. package/dist/adapters/postgresql/tools/backup/dump.js +410 -387
  112. package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
  113. package/dist/adapters/postgresql/tools/backup/planning.d.ts.map +1 -1
  114. package/dist/adapters/postgresql/tools/backup/planning.js +175 -172
  115. package/dist/adapters/postgresql/tools/backup/planning.js.map +1 -1
  116. package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -1
  117. package/dist/adapters/postgresql/tools/citext.js +221 -163
  118. package/dist/adapters/postgresql/tools/citext.js.map +1 -1
  119. package/dist/adapters/postgresql/tools/core/convenience.d.ts +9 -1
  120. package/dist/adapters/postgresql/tools/core/convenience.d.ts.map +1 -1
  121. package/dist/adapters/postgresql/tools/core/convenience.js +96 -9
  122. package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -1
  123. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts +48 -0
  124. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts.map +1 -0
  125. package/dist/adapters/postgresql/tools/core/error-helpers.js +256 -0
  126. package/dist/adapters/postgresql/tools/core/error-helpers.js.map +1 -0
  127. package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -1
  128. package/dist/adapters/postgresql/tools/core/health.js +23 -6
  129. package/dist/adapters/postgresql/tools/core/health.js.map +1 -1
  130. package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -1
  131. package/dist/adapters/postgresql/tools/core/indexes.js +45 -4
  132. package/dist/adapters/postgresql/tools/core/indexes.js.map +1 -1
  133. package/dist/adapters/postgresql/tools/core/objects.d.ts.map +1 -1
  134. package/dist/adapters/postgresql/tools/core/objects.js +104 -85
  135. package/dist/adapters/postgresql/tools/core/objects.js.map +1 -1
  136. package/dist/adapters/postgresql/tools/core/query.d.ts.map +1 -1
  137. package/dist/adapters/postgresql/tools/core/query.js +100 -42
  138. package/dist/adapters/postgresql/tools/core/query.js.map +1 -1
  139. package/dist/adapters/postgresql/tools/core/schemas.d.ts +52 -25
  140. package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
  141. package/dist/adapters/postgresql/tools/core/schemas.js +55 -25
  142. package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -1
  143. package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -1
  144. package/dist/adapters/postgresql/tools/core/tables.js +74 -30
  145. package/dist/adapters/postgresql/tools/core/tables.js.map +1 -1
  146. package/dist/adapters/postgresql/tools/cron.d.ts.map +1 -1
  147. package/dist/adapters/postgresql/tools/cron.js +274 -179
  148. package/dist/adapters/postgresql/tools/cron.js.map +1 -1
  149. package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +1 -1
  150. package/dist/adapters/postgresql/tools/jsonb/advanced.js +372 -284
  151. package/dist/adapters/postgresql/tools/jsonb/advanced.js.map +1 -1
  152. package/dist/adapters/postgresql/tools/jsonb/basic.d.ts.map +1 -1
  153. package/dist/adapters/postgresql/tools/jsonb/basic.js +617 -398
  154. package/dist/adapters/postgresql/tools/jsonb/basic.js.map +1 -1
  155. package/dist/adapters/postgresql/tools/kcache.d.ts.map +1 -1
  156. package/dist/adapters/postgresql/tools/kcache.js +282 -220
  157. package/dist/adapters/postgresql/tools/kcache.js.map +1 -1
  158. package/dist/adapters/postgresql/tools/ltree.d.ts.map +1 -1
  159. package/dist/adapters/postgresql/tools/ltree.js +126 -35
  160. package/dist/adapters/postgresql/tools/ltree.js.map +1 -1
  161. package/dist/adapters/postgresql/tools/monitoring.d.ts.map +1 -1
  162. package/dist/adapters/postgresql/tools/monitoring.js +59 -40
  163. package/dist/adapters/postgresql/tools/monitoring.js.map +1 -1
  164. package/dist/adapters/postgresql/tools/partitioning.d.ts.map +1 -1
  165. package/dist/adapters/postgresql/tools/partitioning.js +150 -15
  166. package/dist/adapters/postgresql/tools/partitioning.js.map +1 -1
  167. package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -1
  168. package/dist/adapters/postgresql/tools/partman/management.js +12 -5
  169. package/dist/adapters/postgresql/tools/partman/management.js.map +1 -1
  170. package/dist/adapters/postgresql/tools/partman/operations.d.ts.map +1 -1
  171. package/dist/adapters/postgresql/tools/partman/operations.js +135 -22
  172. package/dist/adapters/postgresql/tools/partman/operations.js.map +1 -1
  173. package/dist/adapters/postgresql/tools/performance/analysis.d.ts.map +1 -1
  174. package/dist/adapters/postgresql/tools/performance/analysis.js +264 -160
  175. package/dist/adapters/postgresql/tools/performance/analysis.js.map +1 -1
  176. package/dist/adapters/postgresql/tools/performance/explain.d.ts.map +1 -1
  177. package/dist/adapters/postgresql/tools/performance/explain.js +61 -21
  178. package/dist/adapters/postgresql/tools/performance/explain.js.map +1 -1
  179. package/dist/adapters/postgresql/tools/performance/monitoring.d.ts.map +1 -1
  180. package/dist/adapters/postgresql/tools/performance/monitoring.js +44 -7
  181. package/dist/adapters/postgresql/tools/performance/monitoring.js.map +1 -1
  182. package/dist/adapters/postgresql/tools/performance/optimization.d.ts.map +1 -1
  183. package/dist/adapters/postgresql/tools/performance/optimization.js +92 -81
  184. package/dist/adapters/postgresql/tools/performance/optimization.js.map +1 -1
  185. package/dist/adapters/postgresql/tools/performance/stats.d.ts.map +1 -1
  186. package/dist/adapters/postgresql/tools/performance/stats.js +128 -37
  187. package/dist/adapters/postgresql/tools/performance/stats.js.map +1 -1
  188. package/dist/adapters/postgresql/tools/pgcrypto.d.ts.map +1 -1
  189. package/dist/adapters/postgresql/tools/pgcrypto.js +242 -87
  190. package/dist/adapters/postgresql/tools/pgcrypto.js.map +1 -1
  191. package/dist/adapters/postgresql/tools/postgis/advanced.d.ts.map +1 -1
  192. package/dist/adapters/postgresql/tools/postgis/advanced.js +293 -201
  193. package/dist/adapters/postgresql/tools/postgis/advanced.js.map +1 -1
  194. package/dist/adapters/postgresql/tools/postgis/basic.d.ts.map +1 -1
  195. package/dist/adapters/postgresql/tools/postgis/basic.js +359 -249
  196. package/dist/adapters/postgresql/tools/postgis/basic.js.map +1 -1
  197. package/dist/adapters/postgresql/tools/postgis/standalone.d.ts.map +1 -1
  198. package/dist/adapters/postgresql/tools/postgis/standalone.js +135 -51
  199. package/dist/adapters/postgresql/tools/postgis/standalone.js.map +1 -1
  200. package/dist/adapters/postgresql/tools/schema.d.ts.map +1 -1
  201. package/dist/adapters/postgresql/tools/schema.js +515 -226
  202. package/dist/adapters/postgresql/tools/schema.js.map +1 -1
  203. package/dist/adapters/postgresql/tools/stats/advanced.d.ts.map +1 -1
  204. package/dist/adapters/postgresql/tools/stats/advanced.js +515 -476
  205. package/dist/adapters/postgresql/tools/stats/advanced.js.map +1 -1
  206. package/dist/adapters/postgresql/tools/stats/basic.d.ts.map +1 -1
  207. package/dist/adapters/postgresql/tools/stats/basic.js +302 -293
  208. package/dist/adapters/postgresql/tools/stats/basic.js.map +1 -1
  209. package/dist/adapters/postgresql/tools/text.d.ts.map +1 -1
  210. package/dist/adapters/postgresql/tools/text.js +398 -220
  211. package/dist/adapters/postgresql/tools/text.js.map +1 -1
  212. package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -1
  213. package/dist/adapters/postgresql/tools/transactions.js +157 -50
  214. package/dist/adapters/postgresql/tools/transactions.js.map +1 -1
  215. package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +1 -1
  216. package/dist/adapters/postgresql/tools/vector/advanced.js +70 -38
  217. package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -1
  218. package/dist/adapters/postgresql/tools/vector/basic.d.ts +8 -0
  219. package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -1
  220. package/dist/adapters/postgresql/tools/vector/basic.js +194 -82
  221. package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -1
  222. package/dist/cli/args.d.ts +2 -0
  223. package/dist/cli/args.d.ts.map +1 -1
  224. package/dist/cli/args.js +15 -0
  225. package/dist/cli/args.js.map +1 -1
  226. package/dist/cli.js +7 -6
  227. package/dist/cli.js.map +1 -1
  228. package/dist/codemode/api.d.ts.map +1 -1
  229. package/dist/codemode/api.js +4 -3
  230. package/dist/codemode/api.js.map +1 -1
  231. package/dist/constants/ServerInstructions.d.ts +1 -1
  232. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  233. package/dist/constants/ServerInstructions.js +76 -34
  234. package/dist/constants/ServerInstructions.js.map +1 -1
  235. package/dist/filtering/ToolConstants.d.ts +29 -13
  236. package/dist/filtering/ToolConstants.d.ts.map +1 -1
  237. package/dist/filtering/ToolConstants.js +44 -27
  238. package/dist/filtering/ToolConstants.js.map +1 -1
  239. package/dist/utils/logger.js +2 -2
  240. package/dist/utils/logger.js.map +1 -1
  241. package/dist/utils/progress-utils.js +1 -1
  242. package/dist/utils/progress-utils.js.map +1 -1
  243. package/package.json +13 -9
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { readOnly } from "../../../../utils/annotations.js";
7
7
  import { getToolIcons } from "../../../../utils/icons.js";
8
+ import { formatPostgresError } from "../core/error-helpers.js";
8
9
  import {
9
10
  // Base schemas for MCP visibility
10
11
  StatsDescriptiveSchemaBase, StatsPercentilesSchemaBase, StatsCorrelationSchemaBase, StatsRegressionSchemaBase,
@@ -28,60 +29,61 @@ export function createStatsDescriptiveTool(adapter) {
28
29
  annotations: readOnly("Descriptive Statistics"),
29
30
  icons: getToolIcons("stats", readOnly("Descriptive Statistics")),
30
31
  handler: async (params, _context) => {
31
- const { table, column, schema, where, params: queryParams, groupBy, } = StatsDescriptiveSchema.parse(params);
32
- const schemaPrefix = schema ? `"${schema}".` : "";
33
- const whereClause = where ? `WHERE ${where}` : "";
34
- // Validate column is numeric type
35
- const typeCheckQuery = `
32
+ try {
33
+ const { table, column, schema, where, params: queryParams, groupBy, } = StatsDescriptiveSchema.parse(params);
34
+ const schemaPrefix = schema ? `"${schema}".` : "";
35
+ const whereClause = where ? `WHERE ${where}` : "";
36
+ // Validate column is numeric type
37
+ const typeCheckQuery = `
36
38
  SELECT data_type
37
39
  FROM information_schema.columns
38
40
  WHERE table_schema = '${schema ?? "public"}'
39
41
  AND table_name = '${table}'
40
42
  AND column_name = '${column}'
41
43
  `;
42
- const typeResult = await adapter.executeQuery(typeCheckQuery);
43
- const typeRow = typeResult.rows?.[0];
44
- if (!typeRow) {
45
- // Check if table exists
46
- const tableCheckQuery = `
44
+ const typeResult = await adapter.executeQuery(typeCheckQuery);
45
+ const typeRow = typeResult.rows?.[0];
46
+ if (!typeRow) {
47
+ // Check if table exists
48
+ const tableCheckQuery = `
47
49
  SELECT 1 FROM information_schema.tables
48
50
  WHERE table_schema = '${schema ?? "public"}' AND table_name = '${table}'
49
51
  `;
50
- const tableResult = await adapter.executeQuery(tableCheckQuery);
51
- if (tableResult.rows?.length === 0) {
52
- throw new Error(`Table "${schema ?? "public"}.${table}" not found`);
52
+ const tableResult = await adapter.executeQuery(tableCheckQuery);
53
+ if (tableResult.rows?.length === 0) {
54
+ throw new Error(`Table "${schema ?? "public"}.${table}" not found`);
55
+ }
56
+ throw new Error(`Column "${column}" not found in table "${schema ?? "public"}.${table}"`);
53
57
  }
54
- throw new Error(`Column "${column}" not found in table "${schema ?? "public"}.${table}"`);
55
- }
56
- const numericTypes = [
57
- "integer",
58
- "bigint",
59
- "smallint",
60
- "numeric",
61
- "decimal",
62
- "real",
63
- "double precision",
64
- "money",
65
- ];
66
- if (!numericTypes.includes(typeRow.data_type)) {
67
- throw new Error(`Column "${column}" is type "${typeRow.data_type}" but must be a numeric type for statistical analysis`);
68
- }
69
- // Helper to map stats row to numeric object
70
- const mapStats = (row) => ({
71
- count: Number(row["count"]),
72
- min: row["min"] !== null ? Number(row["min"]) : null,
73
- max: row["max"] !== null ? Number(row["max"]) : null,
74
- avg: row["avg"] !== null ? Number(row["avg"]) : null,
75
- stddev: row["stddev"] !== null ? Number(row["stddev"]) : null,
76
- variance: row["variance"] !== null ? Number(row["variance"]) : null,
77
- sum: row["sum"] !== null ? Number(row["sum"]) : null,
78
- mode: row["mode"] !== null && row["mode"] !== undefined
79
- ? Number(row["mode"])
80
- : null,
81
- });
82
- if (groupBy !== undefined) {
83
- // Grouped statistics
84
- const sql = `
58
+ const numericTypes = [
59
+ "integer",
60
+ "bigint",
61
+ "smallint",
62
+ "numeric",
63
+ "decimal",
64
+ "real",
65
+ "double precision",
66
+ "money",
67
+ ];
68
+ if (!numericTypes.includes(typeRow.data_type)) {
69
+ throw new Error(`Column "${column}" is type "${typeRow.data_type}" but must be a numeric type for statistical analysis`);
70
+ }
71
+ // Helper to map stats row to numeric object
72
+ const mapStats = (row) => ({
73
+ count: Number(row["count"]),
74
+ min: row["min"] !== null ? Number(row["min"]) : null,
75
+ max: row["max"] !== null ? Number(row["max"]) : null,
76
+ avg: row["avg"] !== null ? Number(row["avg"]) : null,
77
+ stddev: row["stddev"] !== null ? Number(row["stddev"]) : null,
78
+ variance: row["variance"] !== null ? Number(row["variance"]) : null,
79
+ sum: row["sum"] !== null ? Number(row["sum"]) : null,
80
+ mode: row["mode"] !== null && row["mode"] !== undefined
81
+ ? Number(row["mode"])
82
+ : null,
83
+ });
84
+ if (groupBy !== undefined) {
85
+ // Grouped statistics
86
+ const sql = `
85
87
  SELECT
86
88
  "${groupBy}" as group_key,
87
89
  COUNT("${column}") as count,
@@ -97,24 +99,24 @@ export function createStatsDescriptiveTool(adapter) {
97
99
  GROUP BY "${groupBy}"
98
100
  ORDER BY "${groupBy}"
99
101
  `;
100
- const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
101
- ? [queryParams]
102
- : []));
103
- const rows = result.rows ?? [];
104
- const groups = rows.map((row) => ({
105
- groupKey: row["group_key"],
106
- statistics: mapStats(row),
107
- }));
108
- return {
109
- table: `${schema ?? "public"}.${table}`,
110
- column,
111
- groupBy,
112
- groups,
113
- count: groups.length,
114
- };
115
- }
116
- // Ungrouped statistics (original behavior)
117
- const sql = `
102
+ const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
103
+ ? [queryParams]
104
+ : []));
105
+ const rows = result.rows ?? [];
106
+ const groups = rows.map((row) => ({
107
+ groupKey: row["group_key"],
108
+ statistics: mapStats(row),
109
+ }));
110
+ return {
111
+ table: `${schema ?? "public"}.${table}`,
112
+ column,
113
+ groupBy,
114
+ groups,
115
+ count: groups.length,
116
+ };
117
+ }
118
+ // Ungrouped statistics (original behavior)
119
+ const sql = `
118
120
  SELECT
119
121
  COUNT("${column}") as count,
120
122
  MIN("${column}") as min,
@@ -127,17 +129,24 @@ export function createStatsDescriptiveTool(adapter) {
127
129
  FROM ${schemaPrefix}"${table}"
128
130
  ${whereClause}
129
131
  `;
130
- const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
131
- ? [queryParams]
132
- : []));
133
- const stats = result.rows?.[0];
134
- if (!stats)
135
- throw new Error("No stats found");
136
- return {
137
- table: `${schema ?? "public"}.${table}`,
138
- column,
139
- statistics: mapStats(stats),
140
- };
132
+ const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
133
+ ? [queryParams]
134
+ : []));
135
+ const stats = result.rows?.[0];
136
+ if (!stats)
137
+ throw new Error("No stats found");
138
+ return {
139
+ table: `${schema ?? "public"}.${table}`,
140
+ column,
141
+ statistics: mapStats(stats),
142
+ };
143
+ }
144
+ catch (error) {
145
+ return {
146
+ success: false,
147
+ error: formatPostgresError(error, { tool: "pg_stats_descriptive" }),
148
+ };
149
+ }
141
150
  },
142
151
  };
143
152
  }
@@ -194,32 +203,33 @@ export function createStatsPercentilesTool(adapter) {
194
203
  annotations: readOnly("Percentiles"),
195
204
  icons: getToolIcons("stats", readOnly("Percentiles")),
196
205
  handler: async (params, _context) => {
197
- const parsed = StatsPercentilesSchema.parse(params);
198
- const { table, column, percentiles, schema, where, params: queryParams, groupBy, _percentileScaleWarning, } = parsed;
199
- const schemaName = schema ?? "public";
200
- // Validate column exists and is numeric
201
- await validateNumericColumn(adapter, table, column, schemaName);
202
- const pctiles = percentiles ?? [0.25, 0.5, 0.75];
203
- const schemaPrefix = schema ? `"${schema}".` : "";
204
- const whereClause = where ? `WHERE ${where}` : "";
205
- const percentileSelects = pctiles
206
- .map((p) => `PERCENTILE_CONT(${String(p)}) WITHIN GROUP (ORDER BY "${column}") as p${String(Math.round(p * 100))}`)
207
- .join(",\n ");
208
- // Helper to map row to percentile results (round to 6 decimal places to avoid floating-point artifacts)
209
- const mapPercentiles = (row) => {
210
- const result = {};
211
- for (const p of pctiles) {
212
- const key = `p${String(Math.round(p * 100))}`;
213
- const val = row[key] !== null && row[key] !== undefined
214
- ? Number(row[key])
215
- : null;
216
- result[key] = val !== null ? Math.round(val * 1e6) / 1e6 : null;
217
- }
218
- return result;
219
- };
220
- if (groupBy !== undefined) {
221
- // Grouped percentiles
222
- const sql = `
206
+ try {
207
+ const parsed = StatsPercentilesSchema.parse(params);
208
+ const { table, column, percentiles, schema, where, params: queryParams, groupBy, _percentileScaleWarning, } = parsed;
209
+ const schemaName = schema ?? "public";
210
+ // Validate column exists and is numeric
211
+ await validateNumericColumn(adapter, table, column, schemaName);
212
+ const pctiles = percentiles ?? [0.25, 0.5, 0.75];
213
+ const schemaPrefix = schema ? `"${schema}".` : "";
214
+ const whereClause = where ? `WHERE ${where}` : "";
215
+ const percentileSelects = pctiles
216
+ .map((p) => `PERCENTILE_CONT(${String(p)}) WITHIN GROUP (ORDER BY "${column}") as p${String(Math.round(p * 100))}`)
217
+ .join(",\n ");
218
+ // Helper to map row to percentile results (round to 6 decimal places to avoid floating-point artifacts)
219
+ const mapPercentiles = (row) => {
220
+ const result = {};
221
+ for (const p of pctiles) {
222
+ const key = `p${String(Math.round(p * 100))}`;
223
+ const val = row[key] !== null && row[key] !== undefined
224
+ ? Number(row[key])
225
+ : null;
226
+ result[key] = val !== null ? Math.round(val * 1e6) / 1e6 : null;
227
+ }
228
+ return result;
229
+ };
230
+ if (groupBy !== undefined) {
231
+ // Grouped percentiles
232
+ const sql = `
223
233
  SELECT
224
234
  "${groupBy}" as group_key,
225
235
  ${percentileSelects}
@@ -228,20 +238,42 @@ export function createStatsPercentilesTool(adapter) {
228
238
  GROUP BY "${groupBy}"
229
239
  ORDER BY "${groupBy}"
230
240
  `;
241
+ const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
242
+ ? [queryParams]
243
+ : []));
244
+ const rows = result.rows ?? [];
245
+ const groups = rows.map((row) => ({
246
+ groupKey: row["group_key"],
247
+ percentiles: mapPercentiles(row),
248
+ }));
249
+ const response = {
250
+ table: `${schema ?? "public"}.${table}`,
251
+ column,
252
+ groupBy,
253
+ groups,
254
+ count: groups.length,
255
+ };
256
+ // Include warning if mixed scales were detected
257
+ if (_percentileScaleWarning) {
258
+ response["warning"] = _percentileScaleWarning;
259
+ }
260
+ return response;
261
+ }
262
+ // Ungrouped percentiles
263
+ const sql = `
264
+ SELECT
265
+ ${percentileSelects}
266
+ FROM ${schemaPrefix}"${table}"
267
+ ${whereClause}
268
+ `;
231
269
  const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
232
270
  ? [queryParams]
233
271
  : []));
234
- const rows = result.rows ?? [];
235
- const groups = rows.map((row) => ({
236
- groupKey: row["group_key"],
237
- percentiles: mapPercentiles(row),
238
- }));
272
+ const row = result.rows?.[0] ?? {};
239
273
  const response = {
240
274
  table: `${schema ?? "public"}.${table}`,
241
275
  column,
242
- groupBy,
243
- groups,
244
- count: groups.length,
276
+ percentiles: mapPercentiles(row),
245
277
  };
246
278
  // Include warning if mixed scales were detected
247
279
  if (_percentileScaleWarning) {
@@ -249,27 +281,12 @@ export function createStatsPercentilesTool(adapter) {
249
281
  }
250
282
  return response;
251
283
  }
252
- // Ungrouped percentiles
253
- const sql = `
254
- SELECT
255
- ${percentileSelects}
256
- FROM ${schemaPrefix}"${table}"
257
- ${whereClause}
258
- `;
259
- const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
260
- ? [queryParams]
261
- : []));
262
- const row = result.rows?.[0] ?? {};
263
- const response = {
264
- table: `${schema ?? "public"}.${table}`,
265
- column,
266
- percentiles: mapPercentiles(row),
267
- };
268
- // Include warning if mixed scales were detected
269
- if (_percentileScaleWarning) {
270
- response["warning"] = _percentileScaleWarning;
284
+ catch (error) {
285
+ return {
286
+ success: false,
287
+ error: formatPostgresError(error, { tool: "pg_stats_percentiles" }),
288
+ };
271
289
  }
272
- return response;
273
290
  },
274
291
  };
275
292
  }
@@ -286,75 +303,52 @@ export function createStatsCorrelationTool(adapter) {
286
303
  annotations: readOnly("Correlation Analysis"),
287
304
  icons: getToolIcons("stats", readOnly("Correlation Analysis")),
288
305
  handler: async (params, _context) => {
289
- const parsed = StatsCorrelationSchema.parse(params);
290
- const { table, column1, column2, schema, where, params: queryParams, groupBy, } = parsed;
291
- const schemaPrefix = schema ? `"${schema}".` : "";
292
- const whereClause = where ? `WHERE ${where}` : "";
293
- // Validate both columns are numeric types
294
- const numericTypes = [
295
- "integer",
296
- "bigint",
297
- "smallint",
298
- "numeric",
299
- "decimal",
300
- "real",
301
- "double precision",
302
- "money",
303
- ];
304
- for (const col of [column1, column2]) {
305
- const typeCheckQuery = `
306
- SELECT data_type
307
- FROM information_schema.columns
308
- WHERE table_schema = '${schema ?? "public"}'
309
- AND table_name = '${table}'
310
- AND column_name = '${col}'
311
- `;
312
- const typeResult = await adapter.executeQuery(typeCheckQuery);
313
- const typeRow = typeResult.rows?.[0];
314
- if (!typeRow) {
315
- throw new Error(`Column "${col}" not found in table "${schema ?? "public"}.${table}"`);
316
- }
317
- if (!numericTypes.includes(typeRow.data_type)) {
318
- throw new Error(`Column "${col}" is type "${typeRow.data_type}" but must be numeric for correlation analysis`);
319
- }
320
- }
321
- // Helper to interpret correlation
322
- const interpretCorr = (corr) => {
323
- if (corr === null)
324
- return "N/A";
325
- const absCorr = Math.abs(corr);
326
- let interpretation;
327
- if (absCorr >= 0.9)
328
- interpretation = "Very strong";
329
- else if (absCorr >= 0.7)
330
- interpretation = "Strong";
331
- else if (absCorr >= 0.5)
332
- interpretation = "Moderate";
333
- else if (absCorr >= 0.3)
334
- interpretation = "Weak";
335
- else
336
- interpretation = "Very weak or no correlation";
337
- interpretation += corr < 0 ? " (negative)" : " (positive)";
338
- return interpretation;
339
- };
340
- // Helper to map row to correlation result
341
- const mapCorrelation = (row) => {
342
- const corr = row["correlation"] !== null ? Number(row["correlation"]) : null;
343
- return {
344
- correlation: corr,
345
- interpretation: interpretCorr(corr),
346
- covariancePopulation: row["covariance_pop"] !== null
347
- ? Number(row["covariance_pop"])
348
- : null,
349
- covarianceSample: row["covariance_sample"] !== null
350
- ? Number(row["covariance_sample"])
351
- : null,
352
- sampleSize: Number(row["sample_size"]),
306
+ try {
307
+ const parsed = StatsCorrelationSchema.parse(params);
308
+ const { table, column1, column2, schema, where, params: queryParams, groupBy, } = parsed;
309
+ const schemaName = schema ?? "public";
310
+ const schemaPrefix = schema ? `"${schema}".` : "";
311
+ const whereClause = where ? `WHERE ${where}` : "";
312
+ // Validate both columns exist and are numeric (with table-first error checking)
313
+ await validateNumericColumn(adapter, table, column1, schemaName);
314
+ await validateNumericColumn(adapter, table, column2, schemaName);
315
+ // Helper to interpret correlation
316
+ const interpretCorr = (corr) => {
317
+ if (corr === null)
318
+ return "N/A";
319
+ const absCorr = Math.abs(corr);
320
+ let interpretation;
321
+ if (absCorr >= 0.9)
322
+ interpretation = "Very strong";
323
+ else if (absCorr >= 0.7)
324
+ interpretation = "Strong";
325
+ else if (absCorr >= 0.5)
326
+ interpretation = "Moderate";
327
+ else if (absCorr >= 0.3)
328
+ interpretation = "Weak";
329
+ else
330
+ interpretation = "Very weak or no correlation";
331
+ interpretation += corr < 0 ? " (negative)" : " (positive)";
332
+ return interpretation;
353
333
  };
354
- };
355
- if (groupBy !== undefined) {
356
- // Grouped correlation
357
- const sql = `
334
+ // Helper to map row to correlation result
335
+ const mapCorrelation = (row) => {
336
+ const corr = row["correlation"] !== null ? Number(row["correlation"]) : null;
337
+ return {
338
+ correlation: corr,
339
+ interpretation: interpretCorr(corr),
340
+ covariancePopulation: row["covariance_pop"] !== null
341
+ ? Number(row["covariance_pop"])
342
+ : null,
343
+ covarianceSample: row["covariance_sample"] !== null
344
+ ? Number(row["covariance_sample"])
345
+ : null,
346
+ sampleSize: Number(row["sample_size"]),
347
+ };
348
+ };
349
+ if (groupBy !== undefined) {
350
+ // Grouped correlation
351
+ const sql = `
358
352
  SELECT
359
353
  "${groupBy}" as group_key,
360
354
  CORR("${column1}", "${column2}")::numeric(10,6) as correlation,
@@ -366,24 +360,24 @@ export function createStatsCorrelationTool(adapter) {
366
360
  GROUP BY "${groupBy}"
367
361
  ORDER BY "${groupBy}"
368
362
  `;
369
- const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
370
- ? [queryParams]
371
- : []));
372
- const rows = result.rows ?? [];
373
- const groups = rows.map((row) => ({
374
- groupKey: row["group_key"],
375
- ...mapCorrelation(row),
376
- }));
377
- return {
378
- table: `${schema ?? "public"}.${table}`,
379
- columns: [column1, column2],
380
- groupBy,
381
- groups,
382
- count: groups.length,
383
- };
384
- }
385
- // Ungrouped correlation
386
- const sql = `
363
+ const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
364
+ ? [queryParams]
365
+ : []));
366
+ const rows = result.rows ?? [];
367
+ const groups = rows.map((row) => ({
368
+ groupKey: row["group_key"],
369
+ ...mapCorrelation(row),
370
+ }));
371
+ return {
372
+ table: `${schema ?? "public"}.${table}`,
373
+ columns: [column1, column2],
374
+ groupBy,
375
+ groups,
376
+ count: groups.length,
377
+ };
378
+ }
379
+ // Ungrouped correlation
380
+ const sql = `
387
381
  SELECT
388
382
  CORR("${column1}", "${column2}")::numeric(10,6) as correlation,
389
383
  COVAR_POP("${column1}", "${column2}")::numeric(20,6) as covariance_pop,
@@ -392,22 +386,29 @@ export function createStatsCorrelationTool(adapter) {
392
386
  FROM ${schemaPrefix}"${table}"
393
387
  ${whereClause}
394
388
  `;
395
- const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
396
- ? [queryParams]
397
- : []));
398
- const row = result.rows?.[0];
399
- if (!row)
400
- throw new Error("No correlation data found");
401
- const response = {
402
- table: `${schema ?? "public"}.${table}`,
403
- columns: [column1, column2],
404
- ...mapCorrelation(row),
405
- };
406
- // Add note for self-correlation
407
- if (column1 === column2) {
408
- response["note"] = "Self-correlation always equals 1.0";
389
+ const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
390
+ ? [queryParams]
391
+ : []));
392
+ const row = result.rows?.[0];
393
+ if (!row)
394
+ throw new Error("No correlation data found");
395
+ const response = {
396
+ table: `${schema ?? "public"}.${table}`,
397
+ columns: [column1, column2],
398
+ ...mapCorrelation(row),
399
+ };
400
+ // Add note for self-correlation
401
+ if (column1 === column2) {
402
+ response["note"] = "Self-correlation always equals 1.0";
403
+ }
404
+ return response;
405
+ }
406
+ catch (error) {
407
+ return {
408
+ success: false,
409
+ error: formatPostgresError(error, { tool: "pg_stats_correlation" }),
410
+ };
409
411
  }
410
- return response;
411
412
  },
412
413
  };
413
414
  }
@@ -424,37 +425,38 @@ export function createStatsRegressionTool(adapter) {
424
425
  annotations: readOnly("Linear Regression"),
425
426
  icons: getToolIcons("stats", readOnly("Linear Regression")),
426
427
  handler: async (params, _context) => {
427
- const parsed = StatsRegressionSchema.parse(params);
428
- const { table, xColumn, yColumn, schema, where, params: queryParams, groupBy, } = parsed;
429
- const schemaName = schema ?? "public";
430
- const schemaPrefix = schema ? `"${schema}".` : "";
431
- const whereClause = where ? `WHERE ${where}` : "";
432
- // Validate both columns exist and are numeric
433
- await validateNumericColumn(adapter, table, xColumn, schemaName);
434
- await validateNumericColumn(adapter, table, yColumn, schemaName);
435
- // Helper to map row to regression result
436
- const mapRegression = (row) => {
437
- const slope = row["slope"] !== null ? Number(row["slope"]) : null;
438
- const intercept = row["intercept"] !== null ? Number(row["intercept"]) : null;
439
- const rSquared = row["r_squared"] !== null ? Number(row["r_squared"]) : null;
440
- let equation = "N/A";
441
- if (slope !== null && intercept !== null) {
442
- const sign = intercept >= 0 ? "+" : "-";
443
- equation = `y = ${slope.toFixed(4)}x ${sign} ${Math.abs(intercept).toFixed(4)}`;
444
- }
445
- return {
446
- slope,
447
- intercept,
448
- rSquared,
449
- equation,
450
- avgX: row["avg_x"] !== null ? Number(row["avg_x"]) : null,
451
- avgY: row["avg_y"] !== null ? Number(row["avg_y"]) : null,
452
- sampleSize: Number(row["sample_size"]),
428
+ try {
429
+ const parsed = StatsRegressionSchema.parse(params);
430
+ const { table, xColumn, yColumn, schema, where, params: queryParams, groupBy, } = parsed;
431
+ const schemaName = schema ?? "public";
432
+ const schemaPrefix = schema ? `"${schema}".` : "";
433
+ const whereClause = where ? `WHERE ${where}` : "";
434
+ // Validate both columns exist and are numeric
435
+ await validateNumericColumn(adapter, table, xColumn, schemaName);
436
+ await validateNumericColumn(adapter, table, yColumn, schemaName);
437
+ // Helper to map row to regression result
438
+ const mapRegression = (row) => {
439
+ const slope = row["slope"] !== null ? Number(row["slope"]) : null;
440
+ const intercept = row["intercept"] !== null ? Number(row["intercept"]) : null;
441
+ const rSquared = row["r_squared"] !== null ? Number(row["r_squared"]) : null;
442
+ let equation = "N/A";
443
+ if (slope !== null && intercept !== null) {
444
+ const sign = intercept >= 0 ? "+" : "-";
445
+ equation = `y = ${slope.toFixed(4)}x ${sign} ${Math.abs(intercept).toFixed(4)}`;
446
+ }
447
+ return {
448
+ slope,
449
+ intercept,
450
+ rSquared,
451
+ equation,
452
+ avgX: row["avg_x"] !== null ? Number(row["avg_x"]) : null,
453
+ avgY: row["avg_y"] !== null ? Number(row["avg_y"]) : null,
454
+ sampleSize: Number(row["sample_size"]),
455
+ };
453
456
  };
454
- };
455
- if (groupBy !== undefined) {
456
- // Grouped regression
457
- const sql = `
457
+ if (groupBy !== undefined) {
458
+ // Grouped regression
459
+ const sql = `
458
460
  SELECT
459
461
  "${groupBy}" as group_key,
460
462
  REGR_SLOPE("${yColumn}", "${xColumn}")::numeric(20,6) as slope,
@@ -468,25 +470,25 @@ export function createStatsRegressionTool(adapter) {
468
470
  GROUP BY "${groupBy}"
469
471
  ORDER BY "${groupBy}"
470
472
  `;
471
- const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
472
- ? [queryParams]
473
- : []));
474
- const rows = result.rows ?? [];
475
- const groups = rows.map((row) => ({
476
- groupKey: row["group_key"],
477
- regression: mapRegression(row),
478
- }));
479
- return {
480
- table: `${schema ?? "public"}.${table}`,
481
- xColumn,
482
- yColumn,
483
- groupBy,
484
- groups,
485
- count: groups.length,
486
- };
487
- }
488
- // Ungrouped regression
489
- const sql = `
473
+ const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
474
+ ? [queryParams]
475
+ : []));
476
+ const rows = result.rows ?? [];
477
+ const groups = rows.map((row) => ({
478
+ groupKey: row["group_key"],
479
+ regression: mapRegression(row),
480
+ }));
481
+ return {
482
+ table: `${schema ?? "public"}.${table}`,
483
+ xColumn,
484
+ yColumn,
485
+ groupBy,
486
+ groups,
487
+ count: groups.length,
488
+ };
489
+ }
490
+ // Ungrouped regression
491
+ const sql = `
490
492
  SELECT
491
493
  REGR_SLOPE("${yColumn}", "${xColumn}")::numeric(20,6) as slope,
492
494
  REGR_INTERCEPT("${yColumn}", "${xColumn}")::numeric(20,6) as intercept,
@@ -500,23 +502,30 @@ export function createStatsRegressionTool(adapter) {
500
502
  FROM ${schemaPrefix}"${table}"
501
503
  ${whereClause}
502
504
  `;
503
- const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
504
- ? [queryParams]
505
- : []));
506
- const row = result.rows?.[0];
507
- if (!row)
508
- return { error: "No regression data found" };
509
- const response = {
510
- table: `${schema ?? "public"}.${table}`,
511
- xColumn,
512
- yColumn,
513
- regression: mapRegression(row),
514
- };
515
- // Add note for self-regression
516
- if (xColumn === yColumn) {
517
- response["note"] = "Self-regression always returns slope=1, r²=1";
505
+ const result = await adapter.executeQuery(sql, ...(queryParams !== undefined && queryParams.length > 0
506
+ ? [queryParams]
507
+ : []));
508
+ const row = result.rows?.[0];
509
+ if (!row)
510
+ return { error: "No regression data found" };
511
+ const response = {
512
+ table: `${schema ?? "public"}.${table}`,
513
+ xColumn,
514
+ yColumn,
515
+ regression: mapRegression(row),
516
+ };
517
+ // Add note for self-regression
518
+ if (xColumn === yColumn) {
519
+ response["note"] = "Self-regression always returns slope=1, r²=1";
520
+ }
521
+ return response;
522
+ }
523
+ catch (error) {
524
+ return {
525
+ success: false,
526
+ error: formatPostgresError(error, { tool: "pg_stats_regression" }),
527
+ };
518
528
  }
519
- return response;
520
529
  },
521
530
  };
522
531
  }