@neverinfamous/postgres-mcp 1.2.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 +68 -62
- package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
- package/dist/adapters/postgresql/PostgresAdapter.js +53 -3
- package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
- package/dist/adapters/postgresql/prompts/ltree.js +2 -2
- package/dist/adapters/postgresql/prompts/ltree.js.map +1 -1
- package/dist/adapters/postgresql/schemas/admin.d.ts +10 -5
- package/dist/adapters/postgresql/schemas/admin.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/admin.js +10 -5
- package/dist/adapters/postgresql/schemas/admin.js.map +1 -1
- package/dist/adapters/postgresql/schemas/backup.d.ts +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 +53 -19
- package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/core.js +61 -17
- package/dist/adapters/postgresql/schemas/core.js.map +1 -1
- package/dist/adapters/postgresql/schemas/cron.d.ts +51 -32
- package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/cron.js +64 -44
- package/dist/adapters/postgresql/schemas/cron.js.map +1 -1
- package/dist/adapters/postgresql/schemas/extensions.d.ts +168 -73
- package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/extensions.js +177 -60
- 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 +5 -4
- package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/partitioning.js +5 -4
- package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.d.ts +60 -30
- package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.js +85 -22
- 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 -0
- 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 +11 -4
- 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 +360 -337
- package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
- package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/citext.js +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 +78 -11
- package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/error-helpers.d.ts +48 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.js +256 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/health.js +18 -4
- package/dist/adapters/postgresql/tools/core/health.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/indexes.js +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 +51 -25
- package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/schemas.js +51 -25
- package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/tables.js +68 -28
- 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 +278 -246
- package/dist/adapters/postgresql/tools/kcache.js.map +1 -1
- package/dist/adapters/postgresql/tools/ltree.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/ltree.js +121 -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 +54 -34
- package/dist/adapters/postgresql/tools/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/tools/partitioning.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partitioning.js +79 -15
- package/dist/adapters/postgresql/tools/partitioning.js.map +1 -1
- package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partman/management.js +11 -4
- 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 +132 -19
- 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 +124 -36
- 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 +244 -89
- 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 +285 -222
- 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 +504 -231
- 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 +392 -218
- package/dist/adapters/postgresql/tools/text.js.map +1 -1
- package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/transactions.js +157 -50
- package/dist/adapters/postgresql/tools/transactions.js.map +1 -1
- package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/vector/advanced.js +18 -0
- package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.js +100 -53
- package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -1
- package/dist/codemode/api.js +1 -1
- 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 +45 -7
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/package.json +9 -5
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Text processing, FTS, trigrams, and fuzzy matching.
|
|
5
5
|
* 14 tools total.
|
|
6
6
|
*/
|
|
7
|
-
import { z } from "zod";
|
|
7
|
+
import { z, ZodError } from "zod";
|
|
8
8
|
import { readOnly, write } from "../../../utils/annotations.js";
|
|
9
9
|
import { getToolIcons } from "../../../utils/icons.js";
|
|
10
|
+
import { formatPostgresError } from "./core/error-helpers.js";
|
|
10
11
|
import { sanitizeIdentifier, sanitizeIdentifiers, sanitizeTableName, } from "../../../utils/identifiers.js";
|
|
11
12
|
import { sanitizeFtsConfig } from "../../../utils/fts-config.js";
|
|
12
13
|
import { sanitizeWhereClause } from "../../../utils/where-clause.js";
|
|
@@ -43,43 +44,65 @@ function createTextSearchTool(adapter) {
|
|
|
43
44
|
annotations: readOnly("Full-Text Search"),
|
|
44
45
|
icons: getToolIcons("text", readOnly("Full-Text Search")),
|
|
45
46
|
handler: async (params, _context) => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
47
|
+
try {
|
|
48
|
+
const parsed = TextSearchSchema.parse(params);
|
|
49
|
+
const cfg = sanitizeFtsConfig(parsed.config ?? "english");
|
|
50
|
+
// Handle both column (string) and columns (array) parameters
|
|
51
|
+
// The preprocessor converts column → columns, but we handle both for safety
|
|
52
|
+
let cols;
|
|
53
|
+
if (parsed.columns !== undefined && parsed.columns.length > 0) {
|
|
54
|
+
cols = parsed.columns;
|
|
55
|
+
}
|
|
56
|
+
else if (parsed.column !== undefined) {
|
|
57
|
+
cols = [parsed.column];
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
error: "Either 'columns' (array) or 'column' (string) is required",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Build qualified table name with schema support
|
|
66
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
67
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
68
|
+
if (!resolvedTable) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
error: "Either 'table' or 'tableName' is required",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
75
|
+
const sanitizedCols = sanitizeIdentifiers(cols);
|
|
76
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
77
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
78
|
+
: "*";
|
|
79
|
+
const tsvector = sanitizedCols
|
|
80
|
+
.map((c) => `coalesce(${c}, '')`)
|
|
81
|
+
.join(" || ' ' || ");
|
|
82
|
+
const limitClause = parsed.limit !== undefined && parsed.limit > 0
|
|
83
|
+
? ` LIMIT ${String(parsed.limit)}`
|
|
84
|
+
: "";
|
|
85
|
+
const sql = `SELECT ${selectCols}, ts_rank_cd(to_tsvector('${cfg}', ${tsvector}), plainto_tsquery('${cfg}', $1)) as rank
|
|
78
86
|
FROM ${tableName}
|
|
79
87
|
WHERE to_tsvector('${cfg}', ${tsvector}) @@ plainto_tsquery('${cfg}', $1)
|
|
80
88
|
ORDER BY rank DESC${limitClause}`;
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
const result = await adapter.executeQuery(sql, [parsed.query]);
|
|
90
|
+
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (error instanceof ZodError) {
|
|
94
|
+
return {
|
|
95
|
+
success: false,
|
|
96
|
+
error: `pg_text_search validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: formatPostgresError(error, {
|
|
102
|
+
tool: "pg_text_search",
|
|
103
|
+
}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
83
106
|
},
|
|
84
107
|
};
|
|
85
108
|
}
|
|
@@ -115,42 +138,64 @@ function createTextRankTool(adapter) {
|
|
|
115
138
|
annotations: readOnly("Text Rank"),
|
|
116
139
|
icons: getToolIcons("text", readOnly("Text Rank")),
|
|
117
140
|
handler: async (params, _context) => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
141
|
+
try {
|
|
142
|
+
const parsed = TextRankSchema.parse(params);
|
|
143
|
+
const cfg = sanitizeFtsConfig(parsed.config ?? "english");
|
|
144
|
+
const norm = parsed.normalization ?? 0;
|
|
145
|
+
// Handle both column (string) and columns (array) parameters
|
|
146
|
+
let cols;
|
|
147
|
+
if (parsed.columns !== undefined && parsed.columns.length > 0) {
|
|
148
|
+
cols = parsed.columns;
|
|
149
|
+
}
|
|
150
|
+
else if (parsed.column !== undefined) {
|
|
151
|
+
cols = [parsed.column];
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
error: "Either column or columns parameter is required",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
160
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
161
|
+
if (!resolvedTable) {
|
|
162
|
+
return {
|
|
163
|
+
success: false,
|
|
164
|
+
error: "Either 'table' or 'tableName' is required",
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
168
|
+
const sanitizedCols = sanitizeIdentifiers(cols);
|
|
169
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
170
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
171
|
+
: "*";
|
|
172
|
+
const tsvector = sanitizedCols
|
|
173
|
+
.map((c) => `coalesce(${c}, '')`)
|
|
174
|
+
.join(" || ' ' || ");
|
|
175
|
+
const limitClause = parsed.limit !== undefined && parsed.limit > 0
|
|
176
|
+
? ` LIMIT ${String(parsed.limit)}`
|
|
177
|
+
: "";
|
|
178
|
+
const sql = `SELECT ${selectCols}, ts_rank_cd(to_tsvector('${cfg}', ${tsvector}), plainto_tsquery('${cfg}', $1), ${String(norm)}) as rank
|
|
149
179
|
FROM ${tableName}
|
|
150
180
|
WHERE to_tsvector('${cfg}', ${tsvector}) @@ plainto_tsquery('${cfg}', $1)
|
|
151
181
|
ORDER BY rank DESC${limitClause}`;
|
|
152
|
-
|
|
153
|
-
|
|
182
|
+
const result = await adapter.executeQuery(sql, [parsed.query]);
|
|
183
|
+
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
if (error instanceof ZodError) {
|
|
187
|
+
return {
|
|
188
|
+
success: false,
|
|
189
|
+
error: `pg_text_rank validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
success: false,
|
|
194
|
+
error: formatPostgresError(error, {
|
|
195
|
+
tool: "pg_text_rank",
|
|
196
|
+
}),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
154
199
|
},
|
|
155
200
|
};
|
|
156
201
|
}
|
|
@@ -164,29 +209,48 @@ function createTrigramSimilarityTool(adapter) {
|
|
|
164
209
|
annotations: readOnly("Trigram Similarity"),
|
|
165
210
|
icons: getToolIcons("text", readOnly("Trigram Similarity")),
|
|
166
211
|
handler: async (params, _context) => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
212
|
+
try {
|
|
213
|
+
const parsed = TrigramSimilaritySchema.parse(params);
|
|
214
|
+
const thresh = parsed.threshold ?? 0.3;
|
|
215
|
+
// Default limit to 100 to prevent large payloads
|
|
216
|
+
const limitVal = parsed.limit !== undefined && parsed.limit > 0 ? parsed.limit : 100;
|
|
217
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
218
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
219
|
+
if (!resolvedTable) {
|
|
220
|
+
return {
|
|
221
|
+
success: false,
|
|
222
|
+
error: "Either 'table' or 'tableName' is required",
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
226
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
227
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
228
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
229
|
+
: "*";
|
|
230
|
+
const additionalWhere = parsed.where
|
|
231
|
+
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
232
|
+
: "";
|
|
233
|
+
const sql = `SELECT ${selectCols}, similarity(${columnName}, $1) as similarity
|
|
185
234
|
FROM ${tableName}
|
|
186
235
|
WHERE similarity(${columnName}, $1) > ${String(thresh)}${additionalWhere}
|
|
187
236
|
ORDER BY similarity DESC LIMIT ${String(limitVal)}`;
|
|
188
|
-
|
|
189
|
-
|
|
237
|
+
const result = await adapter.executeQuery(sql, [parsed.value]);
|
|
238
|
+
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
if (error instanceof ZodError) {
|
|
242
|
+
return {
|
|
243
|
+
success: false,
|
|
244
|
+
error: `pg_trigram_similarity validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
success: false,
|
|
249
|
+
error: formatPostgresError(error, {
|
|
250
|
+
tool: "pg_trigram_similarity",
|
|
251
|
+
}),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
190
254
|
},
|
|
191
255
|
};
|
|
192
256
|
}
|
|
@@ -198,7 +262,10 @@ function createFuzzyMatchTool(adapter) {
|
|
|
198
262
|
tableName: z.string().optional().describe("Table name (alias for table)"),
|
|
199
263
|
column: z.string(),
|
|
200
264
|
value: z.string(),
|
|
201
|
-
method: z
|
|
265
|
+
method: z
|
|
266
|
+
.string()
|
|
267
|
+
.optional()
|
|
268
|
+
.describe("Fuzzy match method (default: levenshtein). Valid: soundex, levenshtein, metaphone"),
|
|
202
269
|
maxDistance: z
|
|
203
270
|
.number()
|
|
204
271
|
.optional()
|
|
@@ -225,37 +292,68 @@ function createFuzzyMatchTool(adapter) {
|
|
|
225
292
|
annotations: readOnly("Fuzzy Match"),
|
|
226
293
|
icons: getToolIcons("text", readOnly("Fuzzy Match")),
|
|
227
294
|
handler: async (params, _context) => {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
:
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
295
|
+
try {
|
|
296
|
+
const parsed = FuzzyMatchSchema.parse(params);
|
|
297
|
+
// Validate method (moved from z.enum to handler for structured error)
|
|
298
|
+
const VALID_METHODS = [
|
|
299
|
+
"levenshtein",
|
|
300
|
+
"soundex",
|
|
301
|
+
"metaphone",
|
|
302
|
+
];
|
|
303
|
+
const rawMethod = parsed.method ?? "levenshtein";
|
|
304
|
+
if (!VALID_METHODS.includes(rawMethod)) {
|
|
305
|
+
return {
|
|
306
|
+
success: false,
|
|
307
|
+
error: `Invalid method "${rawMethod}". Valid methods: ${VALID_METHODS.join(", ")}`,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
const method = rawMethod;
|
|
311
|
+
const maxDist = parsed.maxDistance ?? 3;
|
|
312
|
+
// Default limit to 100 to prevent large payloads
|
|
313
|
+
const limitVal = parsed.limit !== undefined && parsed.limit > 0 ? parsed.limit : 100;
|
|
314
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
315
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
316
|
+
if (!resolvedTable) {
|
|
317
|
+
return {
|
|
318
|
+
success: false,
|
|
319
|
+
error: "Either 'table' or 'tableName' is required",
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
323
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
324
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
325
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
326
|
+
: "*";
|
|
327
|
+
const additionalWhere = parsed.where
|
|
328
|
+
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
329
|
+
: "";
|
|
330
|
+
let sql;
|
|
331
|
+
if (method === "soundex") {
|
|
332
|
+
sql = `SELECT ${selectCols}, soundex(${columnName}) as code FROM ${tableName} WHERE soundex(${columnName}) = soundex($1)${additionalWhere} LIMIT ${String(limitVal)}`;
|
|
333
|
+
}
|
|
334
|
+
else if (method === "metaphone") {
|
|
335
|
+
sql = `SELECT ${selectCols}, metaphone(${columnName}, 10) as code FROM ${tableName} WHERE metaphone(${columnName}, 10) = metaphone($1, 10)${additionalWhere} LIMIT ${String(limitVal)}`;
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
sql = `SELECT ${selectCols}, levenshtein(${columnName}, $1) as distance FROM ${tableName} WHERE levenshtein(${columnName}, $1) <= ${String(maxDist)}${additionalWhere} ORDER BY distance LIMIT ${String(limitVal)}`;
|
|
339
|
+
}
|
|
340
|
+
const result = await adapter.executeQuery(sql, [parsed.value]);
|
|
341
|
+
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
250
342
|
}
|
|
251
|
-
|
|
252
|
-
|
|
343
|
+
catch (error) {
|
|
344
|
+
if (error instanceof ZodError) {
|
|
345
|
+
return {
|
|
346
|
+
success: false,
|
|
347
|
+
error: `pg_fuzzy_match validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
success: false,
|
|
352
|
+
error: formatPostgresError(error, {
|
|
353
|
+
tool: "pg_fuzzy_match",
|
|
354
|
+
}),
|
|
355
|
+
};
|
|
253
356
|
}
|
|
254
|
-
else {
|
|
255
|
-
sql = `SELECT ${selectCols}, levenshtein(${columnName}, $1) as distance FROM ${tableName} WHERE levenshtein(${columnName}, $1) <= ${String(maxDist)}${additionalWhere} ORDER BY distance LIMIT ${String(limitVal)}`;
|
|
256
|
-
}
|
|
257
|
-
const result = await adapter.executeQuery(sql, [parsed.value]);
|
|
258
|
-
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
259
357
|
},
|
|
260
358
|
};
|
|
261
359
|
}
|
|
@@ -269,27 +367,46 @@ function createRegexpMatchTool(adapter) {
|
|
|
269
367
|
annotations: readOnly("Regexp Match"),
|
|
270
368
|
icons: getToolIcons("text", readOnly("Regexp Match")),
|
|
271
369
|
handler: async (params, _context) => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
370
|
+
try {
|
|
371
|
+
const parsed = RegexpMatchSchema.parse(params);
|
|
372
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
373
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
374
|
+
if (!resolvedTable) {
|
|
375
|
+
return {
|
|
376
|
+
success: false,
|
|
377
|
+
error: "Either 'table' or 'tableName' is required",
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
381
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
382
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
383
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
384
|
+
: "*";
|
|
385
|
+
const op = parsed.flags?.includes("i") ? "~*" : "~";
|
|
386
|
+
const additionalWhere = parsed.where
|
|
387
|
+
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
388
|
+
: "";
|
|
389
|
+
// Default limit to 100 to prevent large payloads
|
|
390
|
+
const limitVal = parsed.limit !== undefined && parsed.limit > 0 ? parsed.limit : 100;
|
|
391
|
+
const limitClause = ` LIMIT ${String(limitVal)}`;
|
|
392
|
+
const sql = `SELECT ${selectCols} FROM ${tableName} WHERE ${columnName} ${op} $1${additionalWhere}${limitClause}`;
|
|
393
|
+
const result = await adapter.executeQuery(sql, [parsed.pattern]);
|
|
394
|
+
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
if (error instanceof ZodError) {
|
|
398
|
+
return {
|
|
399
|
+
success: false,
|
|
400
|
+
error: `pg_regexp_match validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
success: false,
|
|
405
|
+
error: formatPostgresError(error, {
|
|
406
|
+
tool: "pg_regexp_match",
|
|
407
|
+
}),
|
|
408
|
+
};
|
|
277
409
|
}
|
|
278
|
-
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
279
|
-
const columnName = sanitizeIdentifier(parsed.column);
|
|
280
|
-
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
281
|
-
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
282
|
-
: "*";
|
|
283
|
-
const op = parsed.flags?.includes("i") ? "~*" : "~";
|
|
284
|
-
const additionalWhere = parsed.where
|
|
285
|
-
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
286
|
-
: "";
|
|
287
|
-
// Default limit to 100 to prevent large payloads
|
|
288
|
-
const limitVal = parsed.limit !== undefined && parsed.limit > 0 ? parsed.limit : 100;
|
|
289
|
-
const limitClause = ` LIMIT ${String(limitVal)}`;
|
|
290
|
-
const sql = `SELECT ${selectCols} FROM ${tableName} WHERE ${columnName} ${op} $1${additionalWhere}${limitClause}`;
|
|
291
|
-
const result = await adapter.executeQuery(sql, [parsed.pattern]);
|
|
292
|
-
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
293
410
|
},
|
|
294
411
|
};
|
|
295
412
|
}
|
|
@@ -327,27 +444,46 @@ function createLikeSearchTool(adapter) {
|
|
|
327
444
|
annotations: readOnly("LIKE Search"),
|
|
328
445
|
icons: getToolIcons("text", readOnly("LIKE Search")),
|
|
329
446
|
handler: async (params, _context) => {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
447
|
+
try {
|
|
448
|
+
const parsed = LikeSearchSchema.parse(params);
|
|
449
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
450
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
451
|
+
if (!resolvedTable) {
|
|
452
|
+
return {
|
|
453
|
+
success: false,
|
|
454
|
+
error: "Either 'table' or 'tableName' is required",
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
458
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
459
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
460
|
+
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
461
|
+
: "*";
|
|
462
|
+
const op = parsed.caseSensitive === true ? "LIKE" : "ILIKE";
|
|
463
|
+
const additionalWhere = parsed.where
|
|
464
|
+
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
465
|
+
: "";
|
|
466
|
+
// Default limit to 100 to prevent large payloads
|
|
467
|
+
const limitVal = parsed.limit !== undefined && parsed.limit > 0 ? parsed.limit : 100;
|
|
468
|
+
const limitClause = ` LIMIT ${String(limitVal)}`;
|
|
469
|
+
const sql = `SELECT ${selectCols} FROM ${tableName} WHERE ${columnName} ${op} $1${additionalWhere}${limitClause}`;
|
|
470
|
+
const result = await adapter.executeQuery(sql, [parsed.pattern]);
|
|
471
|
+
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
472
|
+
}
|
|
473
|
+
catch (error) {
|
|
474
|
+
if (error instanceof ZodError) {
|
|
475
|
+
return {
|
|
476
|
+
success: false,
|
|
477
|
+
error: `pg_like_search validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
return {
|
|
481
|
+
success: false,
|
|
482
|
+
error: formatPostgresError(error, {
|
|
483
|
+
tool: "pg_like_search",
|
|
484
|
+
}),
|
|
485
|
+
};
|
|
335
486
|
}
|
|
336
|
-
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
337
|
-
const columnName = sanitizeIdentifier(parsed.column);
|
|
338
|
-
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
339
|
-
? sanitizeIdentifiers(parsed.select).join(", ")
|
|
340
|
-
: "*";
|
|
341
|
-
const op = parsed.caseSensitive === true ? "LIKE" : "ILIKE";
|
|
342
|
-
const additionalWhere = parsed.where
|
|
343
|
-
? ` AND (${sanitizeWhereClause(parsed.where)})`
|
|
344
|
-
: "";
|
|
345
|
-
// Default limit to 100 to prevent large payloads
|
|
346
|
-
const limitVal = parsed.limit !== undefined && parsed.limit > 0 ? parsed.limit : 100;
|
|
347
|
-
const limitClause = ` LIMIT ${String(limitVal)}`;
|
|
348
|
-
const sql = `SELECT ${selectCols} FROM ${tableName} WHERE ${columnName} ${op} $1${additionalWhere}${limitClause}`;
|
|
349
|
-
const result = await adapter.executeQuery(sql, [parsed.pattern]);
|
|
350
|
-
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
351
487
|
},
|
|
352
488
|
};
|
|
353
489
|
}
|
|
@@ -395,40 +531,59 @@ function createTextHeadlineTool(adapter) {
|
|
|
395
531
|
annotations: readOnly("Text Headline"),
|
|
396
532
|
icons: getToolIcons("text", readOnly("Text Headline")),
|
|
397
533
|
handler: async (params, _context) => {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
534
|
+
try {
|
|
535
|
+
const parsed = HeadlineSchema.parse(params);
|
|
536
|
+
const cfg = sanitizeFtsConfig(parsed.config ?? "english");
|
|
537
|
+
// Build options string from individual params or use provided options
|
|
538
|
+
let opts;
|
|
539
|
+
if (parsed.options) {
|
|
540
|
+
opts = parsed.options;
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
const optParts = [];
|
|
544
|
+
optParts.push(`StartSel=${parsed.startSel ?? "<b>"}`);
|
|
545
|
+
optParts.push(`StopSel=${parsed.stopSel ?? "</b>"}`);
|
|
546
|
+
optParts.push(`MaxWords=${String(parsed.maxWords ?? 35)}`);
|
|
547
|
+
optParts.push(`MinWords=${String(parsed.minWords ?? 15)}`);
|
|
548
|
+
opts = optParts.join(", ");
|
|
549
|
+
}
|
|
550
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
551
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
552
|
+
if (!resolvedTable) {
|
|
553
|
+
return {
|
|
554
|
+
success: false,
|
|
555
|
+
error: "Either 'table' or 'tableName' is required",
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
559
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
560
|
+
// Use provided select columns, or default to * (user should specify PK for stable identification)
|
|
561
|
+
const selectCols = parsed.select !== undefined && parsed.select.length > 0
|
|
562
|
+
? sanitizeIdentifiers(parsed.select).join(", ") + ", "
|
|
563
|
+
: "";
|
|
564
|
+
const limitClause = parsed.limit !== undefined && parsed.limit > 0
|
|
565
|
+
? ` LIMIT ${String(parsed.limit)}`
|
|
566
|
+
: "";
|
|
567
|
+
const sql = `SELECT ${selectCols}ts_headline('${cfg}', ${columnName}, plainto_tsquery('${cfg}', $1), '${opts}') as headline
|
|
428
568
|
FROM ${tableName}
|
|
429
569
|
WHERE to_tsvector('${cfg}', ${columnName}) @@ plainto_tsquery('${cfg}', $1)${limitClause}`;
|
|
430
|
-
|
|
431
|
-
|
|
570
|
+
const result = await adapter.executeQuery(sql, [parsed.query]);
|
|
571
|
+
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
if (error instanceof ZodError) {
|
|
575
|
+
return {
|
|
576
|
+
success: false,
|
|
577
|
+
error: `pg_text_headline validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
success: false,
|
|
582
|
+
error: formatPostgresError(error, {
|
|
583
|
+
tool: "pg_text_headline",
|
|
584
|
+
}),
|
|
585
|
+
};
|
|
586
|
+
}
|
|
432
587
|
},
|
|
433
588
|
};
|
|
434
589
|
}
|
|
@@ -461,36 +616,55 @@ function createFtsIndexTool(adapter) {
|
|
|
461
616
|
annotations: write("Create FTS Index"),
|
|
462
617
|
icons: getToolIcons("text", write("Create FTS Index")),
|
|
463
618
|
handler: async (params, _context) => {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
619
|
+
try {
|
|
620
|
+
const parsed = FtsIndexSchema.parse(params);
|
|
621
|
+
const cfg = sanitizeFtsConfig(parsed.config ?? "english");
|
|
622
|
+
// The preprocessor guarantees table is set (converts tableName → table)
|
|
623
|
+
const resolvedTable = parsed.table ?? parsed.tableName;
|
|
624
|
+
if (!resolvedTable) {
|
|
625
|
+
return {
|
|
626
|
+
success: false,
|
|
627
|
+
error: "Either 'table' or 'tableName' is required",
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
const defaultIndexName = `idx_${resolvedTable}_${parsed.column}_fts`;
|
|
631
|
+
const resolvedIndexName = parsed.name ?? defaultIndexName;
|
|
632
|
+
const indexName = sanitizeIdentifier(resolvedIndexName);
|
|
633
|
+
// Default to IF NOT EXISTS for safer operation (skip existing indexes)
|
|
634
|
+
const useIfNotExists = parsed.ifNotExists !== false;
|
|
635
|
+
const ifNotExists = useIfNotExists ? "IF NOT EXISTS " : "";
|
|
636
|
+
// Build qualified table name with schema support
|
|
637
|
+
const tableName = sanitizeTableName(resolvedTable, parsed.schema);
|
|
638
|
+
const columnName = sanitizeIdentifier(parsed.column);
|
|
639
|
+
// Check if index exists before creation (to accurately report 'skipped')
|
|
640
|
+
let existedBefore = false;
|
|
641
|
+
if (useIfNotExists) {
|
|
642
|
+
const checkResult = await adapter.executeQuery(`SELECT 1 FROM pg_indexes WHERE indexname = $1 LIMIT 1`, [resolvedIndexName]);
|
|
643
|
+
existedBefore = (checkResult.rows?.length ?? 0) > 0;
|
|
644
|
+
}
|
|
645
|
+
const sql = `CREATE INDEX ${ifNotExists}${indexName} ON ${tableName} USING gin(to_tsvector('${cfg}', ${columnName}))`;
|
|
646
|
+
await adapter.executeQuery(sql);
|
|
647
|
+
return {
|
|
648
|
+
success: true,
|
|
649
|
+
index: resolvedIndexName,
|
|
650
|
+
config: cfg,
|
|
651
|
+
skipped: existedBefore,
|
|
652
|
+
};
|
|
470
653
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
existedBefore = (checkResult.rows?.length ?? 0) > 0;
|
|
654
|
+
catch (error) {
|
|
655
|
+
if (error instanceof ZodError) {
|
|
656
|
+
return {
|
|
657
|
+
success: false,
|
|
658
|
+
error: `pg_create_fts_index validation error: ${error.issues.map((e) => e.message).join(", ")}`,
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
return {
|
|
662
|
+
success: false,
|
|
663
|
+
error: formatPostgresError(error, {
|
|
664
|
+
tool: "pg_create_fts_index",
|
|
665
|
+
}),
|
|
666
|
+
};
|
|
485
667
|
}
|
|
486
|
-
const sql = `CREATE INDEX ${ifNotExists}${indexName} ON ${tableName} USING gin(to_tsvector('${cfg}', ${columnName}))`;
|
|
487
|
-
await adapter.executeQuery(sql);
|
|
488
|
-
return {
|
|
489
|
-
success: true,
|
|
490
|
-
index: resolvedIndexName,
|
|
491
|
-
config: cfg,
|
|
492
|
-
skipped: existedBefore,
|
|
493
|
-
};
|
|
494
668
|
},
|
|
495
669
|
};
|
|
496
670
|
}
|