@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.
- package/README.md +95 -81
- package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
- package/dist/__tests__/mocks/adapter.js +0 -1
- package/dist/__tests__/mocks/adapter.js.map +1 -1
- package/dist/__tests__/mocks/pool.d.ts.map +1 -1
- package/dist/__tests__/mocks/pool.js +0 -1
- package/dist/__tests__/mocks/pool.js.map +1 -1
- package/dist/adapters/DatabaseAdapter.js +1 -1
- package/dist/adapters/DatabaseAdapter.js.map +1 -1
- package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
- package/dist/adapters/postgresql/PostgresAdapter.js +78 -8
- package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
- package/dist/adapters/postgresql/prompts/backup.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/backup.js +2 -3
- package/dist/adapters/postgresql/prompts/backup.js.map +1 -1
- package/dist/adapters/postgresql/prompts/citext.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/citext.js +3 -4
- package/dist/adapters/postgresql/prompts/citext.js.map +1 -1
- package/dist/adapters/postgresql/prompts/extensionSetup.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/extensionSetup.js +2 -3
- package/dist/adapters/postgresql/prompts/extensionSetup.js.map +1 -1
- package/dist/adapters/postgresql/prompts/health.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/health.js +2 -3
- package/dist/adapters/postgresql/prompts/health.js.map +1 -1
- package/dist/adapters/postgresql/prompts/index.js +20 -27
- package/dist/adapters/postgresql/prompts/index.js.map +1 -1
- package/dist/adapters/postgresql/prompts/indexTuning.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/indexTuning.js +2 -3
- package/dist/adapters/postgresql/prompts/indexTuning.js.map +1 -1
- package/dist/adapters/postgresql/prompts/kcache.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/kcache.js +3 -4
- package/dist/adapters/postgresql/prompts/kcache.js.map +1 -1
- package/dist/adapters/postgresql/prompts/ltree.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/ltree.js +5 -6
- package/dist/adapters/postgresql/prompts/ltree.js.map +1 -1
- package/dist/adapters/postgresql/prompts/partman.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/partman.js +2 -3
- package/dist/adapters/postgresql/prompts/partman.js.map +1 -1
- package/dist/adapters/postgresql/prompts/pgcron.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/pgcron.js +2 -3
- package/dist/adapters/postgresql/prompts/pgcron.js.map +1 -1
- package/dist/adapters/postgresql/prompts/pgcrypto.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/pgcrypto.js +3 -4
- package/dist/adapters/postgresql/prompts/pgcrypto.js.map +1 -1
- package/dist/adapters/postgresql/prompts/pgvector.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/pgvector.js +3 -4
- package/dist/adapters/postgresql/prompts/pgvector.js.map +1 -1
- package/dist/adapters/postgresql/prompts/postgis.d.ts.map +1 -1
- package/dist/adapters/postgresql/prompts/postgis.js +2 -3
- package/dist/adapters/postgresql/prompts/postgis.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 +8 -4
- package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/backup.js +11 -4
- package/dist/adapters/postgresql/schemas/backup.js.map +1 -1
- package/dist/adapters/postgresql/schemas/core.d.ts +54 -19
- package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/core.js +65 -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 +168 -73
- package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/extensions.js +179 -62
- package/dist/adapters/postgresql/schemas/extensions.js.map +1 -1
- package/dist/adapters/postgresql/schemas/index.d.ts +5 -5
- package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/index.js +9 -7
- package/dist/adapters/postgresql/schemas/index.js.map +1 -1
- package/dist/adapters/postgresql/schemas/jsonb.d.ts +94 -42
- package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/jsonb.js +101 -30
- package/dist/adapters/postgresql/schemas/jsonb.js.map +1 -1
- package/dist/adapters/postgresql/schemas/monitoring.d.ts +28 -11
- package/dist/adapters/postgresql/schemas/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/monitoring.js +49 -24
- package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/schemas/partitioning.d.ts +15 -11
- package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/partitioning.js +17 -13
- package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.d.ts +62 -31
- package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.js +86 -24
- 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 +20 -3
- package/dist/adapters/postgresql/schemas/postgis.js.map +1 -1
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +35 -23
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/schema-mgmt.js +69 -26
- 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 +8 -5
- package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/text-search.js +15 -5
- 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 +211 -140
- 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 +410 -387
- package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
- package/dist/adapters/postgresql/tools/backup/planning.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/backup/planning.js +175 -172
- package/dist/adapters/postgresql/tools/backup/planning.js.map +1 -1
- package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/citext.js +221 -163
- package/dist/adapters/postgresql/tools/citext.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 +96 -9
- 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 +23 -6
- 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 +45 -4
- 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 +52 -25
- package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/schemas.js +55 -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 +74 -30
- 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 +274 -179
- package/dist/adapters/postgresql/tools/cron.js.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/advanced.js +372 -284
- 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 +617 -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 +282 -220
- 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 +126 -35
- 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 +59 -40
- 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 +150 -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 +12 -5
- 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 +135 -22
- 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 +264 -160
- 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 +44 -7
- 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 +128 -37
- 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 +242 -87
- 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 +293 -201
- 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 +359 -249
- 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 +515 -226
- 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 +515 -476
- 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 +302 -293
- 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 +398 -220
- 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 +70 -38
- package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.d.ts +8 -0
- package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.js +194 -82
- package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -1
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +15 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli.js +7 -6
- package/dist/cli.js.map +1 -1
- package/dist/codemode/api.d.ts.map +1 -1
- package/dist/codemode/api.js +4 -3
- package/dist/codemode/api.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +1 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +76 -34
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/filtering/ToolConstants.d.ts +29 -13
- package/dist/filtering/ToolConstants.d.ts.map +1 -1
- package/dist/filtering/ToolConstants.js +44 -27
- package/dist/filtering/ToolConstants.js.map +1 -1
- package/dist/utils/logger.js +2 -2
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/progress-utils.js +1 -1
- package/dist/utils/progress-utils.js.map +1 -1
- 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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
-
|
|
456
|
-
|
|
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
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
}
|