@slates-integrations/postgresql 0.2.0-rc.6 → 0.2.0-rc.9
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/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/package.json +3 -0
- package/dist/sourcemap-register.cjs +1 -0
- package/package.json +9 -3
- package/src/auth.ts +0 -134
- package/src/config.ts +0 -16
- package/src/index.ts +0 -36
- package/src/lib/client.ts +0 -799
- package/src/lib/errors.ts +0 -55
- package/src/lib/helpers.ts +0 -48
- package/src/lib/protocol.ts +0 -336
- package/src/spec.ts +0 -13
- package/src/tools/delete-rows.ts +0 -84
- package/src/tools/describe-table.ts +0 -231
- package/src/tools/execute-query.ts +0 -95
- package/src/tools/index.ts +0 -12
- package/src/tools/insert-rows.ts +0 -133
- package/src/tools/list-schemas.ts +0 -80
- package/src/tools/list-tables.ts +0 -119
- package/src/tools/manage-indexes.ts +0 -99
- package/src/tools/manage-roles.ts +0 -214
- package/src/tools/manage-schemas.ts +0 -121
- package/src/tools/manage-table.ts +0 -267
- package/src/tools/manage-views.ts +0 -193
- package/src/tools/update-rows.ts +0 -98
- package/src/triggers/inbound-webhook.ts +0 -67
- package/src/triggers/index.ts +0 -2
- package/src/triggers/table-changes.ts +0 -123
- package/tsconfig.json +0 -23
package/src/lib/errors.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { ServiceError, badRequestError } from '@lowerdeck/error';
|
|
2
|
-
|
|
3
|
-
export let postgresServiceError = (message: string) =>
|
|
4
|
-
new ServiceError(badRequestError({ message }));
|
|
5
|
-
|
|
6
|
-
export let postgresUpstreamError = (
|
|
7
|
-
message: string,
|
|
8
|
-
options: {
|
|
9
|
-
reason?: string;
|
|
10
|
-
code?: string;
|
|
11
|
-
parent?: unknown;
|
|
12
|
-
} = {}
|
|
13
|
-
) => {
|
|
14
|
-
let error = postgresServiceError(message);
|
|
15
|
-
error.data.reason = options.reason ?? 'postgresql_error';
|
|
16
|
-
error.data.upstreamCode = options.code;
|
|
17
|
-
|
|
18
|
-
if (options.parent instanceof Error) {
|
|
19
|
-
error.setParent(options.parent);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return error;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export let toPostgresServiceError = (error: unknown, fallbackMessage: string) => {
|
|
26
|
-
if (error instanceof ServiceError) {
|
|
27
|
-
return error;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (error instanceof Error) {
|
|
31
|
-
return postgresUpstreamError(`${fallbackMessage}: ${error.message}`, {
|
|
32
|
-
parent: error
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return postgresUpstreamError(fallbackMessage);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export let postgresFieldsError = (
|
|
40
|
-
fields: Record<string, string>,
|
|
41
|
-
fallbackMessage = 'Unknown error'
|
|
42
|
-
) => {
|
|
43
|
-
let message = fields['message'] || fallbackMessage;
|
|
44
|
-
if (fields['detail']) message += ` - ${fields['detail']}`;
|
|
45
|
-
if (fields['hint']) message += ` (Hint: ${fields['hint']})`;
|
|
46
|
-
if (fields['position']) message += ` at position ${fields['position']}`;
|
|
47
|
-
|
|
48
|
-
return postgresUpstreamError(
|
|
49
|
-
`PostgreSQL error [${fields['code'] || 'UNKNOWN'}]: ${message}`,
|
|
50
|
-
{
|
|
51
|
-
reason: 'postgresql_server_error',
|
|
52
|
-
code: fields['code']
|
|
53
|
-
}
|
|
54
|
-
);
|
|
55
|
-
};
|
package/src/lib/helpers.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { PostgresClient, type ConnectionConfig } from './client';
|
|
2
|
-
|
|
3
|
-
export interface AuthOutput {
|
|
4
|
-
host: string;
|
|
5
|
-
port: number;
|
|
6
|
-
database: string;
|
|
7
|
-
username: string;
|
|
8
|
-
password: string;
|
|
9
|
-
sslMode: 'disable' | 'require' | 'verify-ca' | 'verify-full';
|
|
10
|
-
connectionString: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface ConfigOutput {
|
|
14
|
-
defaultSchema: string;
|
|
15
|
-
queryTimeout: number;
|
|
16
|
-
maxRows: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export let createClient = (auth: AuthOutput, config: ConfigOutput): PostgresClient => {
|
|
20
|
-
let connectionConfig: ConnectionConfig = {
|
|
21
|
-
host: auth.host,
|
|
22
|
-
port: auth.port,
|
|
23
|
-
database: auth.database,
|
|
24
|
-
username: auth.username,
|
|
25
|
-
password: auth.password,
|
|
26
|
-
sslMode: auth.sslMode,
|
|
27
|
-
queryTimeout: config.queryTimeout
|
|
28
|
-
};
|
|
29
|
-
return new PostgresClient(connectionConfig);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// Escape an identifier (table name, column name, etc.) for safe use in SQL
|
|
33
|
-
export let escapeIdentifier = (name: string): string => {
|
|
34
|
-
return '"' + name.replace(/"/g, '""') + '"';
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Escape a literal value for safe use in SQL
|
|
38
|
-
export let escapeLiteral = (value: string): string => {
|
|
39
|
-
return "'" + value.replace(/'/g, "''") + "'";
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// Build a qualified table name with optional schema
|
|
43
|
-
export let qualifiedTableName = (tableName: string, schemaName?: string): string => {
|
|
44
|
-
if (schemaName) {
|
|
45
|
-
return `${escapeIdentifier(schemaName)}.${escapeIdentifier(tableName)}`;
|
|
46
|
-
}
|
|
47
|
-
return escapeIdentifier(tableName);
|
|
48
|
-
};
|
package/src/lib/protocol.ts
DELETED
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
// PostgreSQL Wire Protocol helpers
|
|
2
|
-
// Implements the minimal subset of the v3 protocol needed for simple query execution
|
|
3
|
-
|
|
4
|
-
export let MessageTypes = {
|
|
5
|
-
// Frontend (client -> server)
|
|
6
|
-
STARTUP: 0,
|
|
7
|
-
PASSWORD: 'p'.charCodeAt(0),
|
|
8
|
-
QUERY: 'Q'.charCodeAt(0),
|
|
9
|
-
TERMINATE: 'X'.charCodeAt(0),
|
|
10
|
-
// Backend (server -> client)
|
|
11
|
-
AUTHENTICATION: 'R'.charCodeAt(0),
|
|
12
|
-
ERROR_RESPONSE: 'E'.charCodeAt(0),
|
|
13
|
-
ROW_DESCRIPTION: 'T'.charCodeAt(0),
|
|
14
|
-
DATA_ROW: 'D'.charCodeAt(0),
|
|
15
|
-
COMMAND_COMPLETE: 'C'.charCodeAt(0),
|
|
16
|
-
READY_FOR_QUERY: 'Z'.charCodeAt(0),
|
|
17
|
-
NOTICE_RESPONSE: 'N'.charCodeAt(0),
|
|
18
|
-
PARAMETER_STATUS: 'S'.charCodeAt(0),
|
|
19
|
-
BACKEND_KEY_DATA: 'K'.charCodeAt(0),
|
|
20
|
-
EMPTY_QUERY_RESPONSE: 'I'.charCodeAt(0)
|
|
21
|
-
} as const;
|
|
22
|
-
|
|
23
|
-
export let AuthenticationTypes = {
|
|
24
|
-
OK: 0,
|
|
25
|
-
CLEARTEXT_PASSWORD: 3,
|
|
26
|
-
MD5_PASSWORD: 5,
|
|
27
|
-
SASL: 10,
|
|
28
|
-
SASL_CONTINUE: 11,
|
|
29
|
-
SASL_FINAL: 12
|
|
30
|
-
} as const;
|
|
31
|
-
|
|
32
|
-
// Simple byte buffer writer
|
|
33
|
-
export class MessageWriter {
|
|
34
|
-
private parts: Uint8Array[] = [];
|
|
35
|
-
private length = 0;
|
|
36
|
-
|
|
37
|
-
writeInt32(val: number): this {
|
|
38
|
-
let buf = new Uint8Array(4);
|
|
39
|
-
let view = new DataView(buf.buffer);
|
|
40
|
-
view.setInt32(0, val, false);
|
|
41
|
-
this.parts.push(buf);
|
|
42
|
-
this.length += 4;
|
|
43
|
-
return this;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
writeInt16(val: number): this {
|
|
47
|
-
let buf = new Uint8Array(2);
|
|
48
|
-
let view = new DataView(buf.buffer);
|
|
49
|
-
view.setInt16(0, val, false);
|
|
50
|
-
this.parts.push(buf);
|
|
51
|
-
this.length += 2;
|
|
52
|
-
return this;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
writeByte(val: number): this {
|
|
56
|
-
let buf = new Uint8Array([val]);
|
|
57
|
-
this.parts.push(buf);
|
|
58
|
-
this.length += 1;
|
|
59
|
-
return this;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
writeCString(val: string): this {
|
|
63
|
-
let encoder = new TextEncoder();
|
|
64
|
-
let encoded = encoder.encode(val);
|
|
65
|
-
let buf = new Uint8Array(encoded.length + 1);
|
|
66
|
-
buf.set(encoded);
|
|
67
|
-
buf[encoded.length] = 0;
|
|
68
|
-
this.parts.push(buf);
|
|
69
|
-
this.length += buf.length;
|
|
70
|
-
return this;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
writeBytes(val: Uint8Array): this {
|
|
74
|
-
this.parts.push(val);
|
|
75
|
-
this.length += val.length;
|
|
76
|
-
return this;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
toBuffer(): Uint8Array {
|
|
80
|
-
let result = new Uint8Array(this.length);
|
|
81
|
-
let offset = 0;
|
|
82
|
-
for (let part of this.parts) {
|
|
83
|
-
result.set(part, offset);
|
|
84
|
-
offset += part.length;
|
|
85
|
-
}
|
|
86
|
-
return result;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
getLength(): number {
|
|
90
|
-
return this.length;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Build startup message (no message type byte, just length + protocol version + params)
|
|
95
|
-
export let buildStartupMessage = (user: string, database: string): Uint8Array => {
|
|
96
|
-
let params = new MessageWriter();
|
|
97
|
-
params.writeInt32(196608); // Protocol version 3.0
|
|
98
|
-
params.writeCString('user');
|
|
99
|
-
params.writeCString(user);
|
|
100
|
-
params.writeCString('database');
|
|
101
|
-
params.writeCString(database);
|
|
102
|
-
params.writeCString('client_encoding');
|
|
103
|
-
params.writeCString('UTF8');
|
|
104
|
-
params.writeByte(0); // Terminator
|
|
105
|
-
|
|
106
|
-
let body = params.toBuffer();
|
|
107
|
-
let msg = new MessageWriter();
|
|
108
|
-
msg.writeInt32(body.length + 4); // length includes itself
|
|
109
|
-
msg.writeBytes(body);
|
|
110
|
-
return msg.toBuffer();
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// Build a typed message (type byte + length + body)
|
|
114
|
-
export let buildMessage = (type: number, body: Uint8Array): Uint8Array => {
|
|
115
|
-
let msg = new MessageWriter();
|
|
116
|
-
msg.writeByte(type);
|
|
117
|
-
msg.writeInt32(body.length + 4); // length includes itself
|
|
118
|
-
msg.writeBytes(body);
|
|
119
|
-
return msg.toBuffer();
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export let buildPasswordMessage = (password: string): Uint8Array => {
|
|
123
|
-
let writer = new MessageWriter();
|
|
124
|
-
writer.writeCString(password);
|
|
125
|
-
return buildMessage(MessageTypes.PASSWORD, writer.toBuffer());
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
export let buildQueryMessage = (query: string): Uint8Array => {
|
|
129
|
-
let writer = new MessageWriter();
|
|
130
|
-
writer.writeCString(query);
|
|
131
|
-
return buildMessage(MessageTypes.QUERY, writer.toBuffer());
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
export let buildTerminateMessage = (): Uint8Array => {
|
|
135
|
-
return buildMessage(MessageTypes.TERMINATE, new Uint8Array(0));
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// Parse incoming messages from a buffer
|
|
139
|
-
export interface ParsedMessage {
|
|
140
|
-
type: number;
|
|
141
|
-
length: number;
|
|
142
|
-
body: Uint8Array;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export let parseMessages = (
|
|
146
|
-
buffer: Uint8Array
|
|
147
|
-
): { messages: ParsedMessage[]; remaining: Uint8Array } => {
|
|
148
|
-
let messages: ParsedMessage[] = [];
|
|
149
|
-
let offset = 0;
|
|
150
|
-
let view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
151
|
-
|
|
152
|
-
while (offset + 5 <= buffer.length) {
|
|
153
|
-
let type = buffer[offset]!;
|
|
154
|
-
let length = view.getInt32(offset + 1, false);
|
|
155
|
-
|
|
156
|
-
if (offset + 1 + length > buffer.length) {
|
|
157
|
-
break; // Incomplete message
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let body = buffer.slice(offset + 5, offset + 1 + length);
|
|
161
|
-
messages.push({ type, length, body });
|
|
162
|
-
offset += 1 + length;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
messages,
|
|
167
|
-
remaining: buffer.slice(offset)
|
|
168
|
-
};
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
// Parse error/notice response fields
|
|
172
|
-
export let parseErrorFields = (body: Uint8Array): Record<string, string> => {
|
|
173
|
-
let fields: Record<string, string> = {};
|
|
174
|
-
let decoder = new TextDecoder();
|
|
175
|
-
let offset = 0;
|
|
176
|
-
|
|
177
|
-
while (offset < body.length) {
|
|
178
|
-
let fieldType = body[offset]!;
|
|
179
|
-
offset++;
|
|
180
|
-
if (fieldType === 0) break;
|
|
181
|
-
|
|
182
|
-
let end = body.indexOf(0, offset);
|
|
183
|
-
if (end === -1) break;
|
|
184
|
-
|
|
185
|
-
let value = decoder.decode(body.slice(offset, end));
|
|
186
|
-
let key = String.fromCharCode(fieldType);
|
|
187
|
-
|
|
188
|
-
// Map common field types
|
|
189
|
-
if (key === 'S') fields['severity'] = value;
|
|
190
|
-
else if (key === 'V') fields['severityNonLocalized'] = value;
|
|
191
|
-
else if (key === 'C') fields['code'] = value;
|
|
192
|
-
else if (key === 'M') fields['message'] = value;
|
|
193
|
-
else if (key === 'D') fields['detail'] = value;
|
|
194
|
-
else if (key === 'H') fields['hint'] = value;
|
|
195
|
-
else if (key === 'P') fields['position'] = value;
|
|
196
|
-
else if (key === 'q') fields['internalPosition'] = value;
|
|
197
|
-
else if (key === 'W') fields['where'] = value;
|
|
198
|
-
else if (key === 's') fields['schema'] = value;
|
|
199
|
-
else if (key === 't') fields['table'] = value;
|
|
200
|
-
else if (key === 'c') fields['column'] = value;
|
|
201
|
-
else if (key === 'd') fields['dataType'] = value;
|
|
202
|
-
else if (key === 'n') fields['constraint'] = value;
|
|
203
|
-
else fields[key] = value;
|
|
204
|
-
|
|
205
|
-
offset = end + 1;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return fields;
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// Parse RowDescription message
|
|
212
|
-
export interface ColumnDescription {
|
|
213
|
-
name: string;
|
|
214
|
-
tableOid: number;
|
|
215
|
-
columnIndex: number;
|
|
216
|
-
typeOid: number;
|
|
217
|
-
typeLength: number;
|
|
218
|
-
typeModifier: number;
|
|
219
|
-
formatCode: number;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export let parseRowDescription = (body: Uint8Array): ColumnDescription[] => {
|
|
223
|
-
let view = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
224
|
-
let decoder = new TextDecoder();
|
|
225
|
-
let fieldCount = view.getInt16(0, false);
|
|
226
|
-
let columns: ColumnDescription[] = [];
|
|
227
|
-
let offset = 2;
|
|
228
|
-
|
|
229
|
-
for (let i = 0; i < fieldCount; i++) {
|
|
230
|
-
let nameEnd = body.indexOf(0, offset);
|
|
231
|
-
let name = decoder.decode(body.slice(offset, nameEnd));
|
|
232
|
-
offset = nameEnd + 1;
|
|
233
|
-
|
|
234
|
-
let tableOid = view.getInt32(offset, false);
|
|
235
|
-
offset += 4;
|
|
236
|
-
let columnIndex = view.getInt16(offset, false);
|
|
237
|
-
offset += 2;
|
|
238
|
-
let typeOid = view.getInt32(offset, false);
|
|
239
|
-
offset += 4;
|
|
240
|
-
let typeLength = view.getInt16(offset, false);
|
|
241
|
-
offset += 2;
|
|
242
|
-
let typeModifier = view.getInt32(offset, false);
|
|
243
|
-
offset += 4;
|
|
244
|
-
let formatCode = view.getInt16(offset, false);
|
|
245
|
-
offset += 2;
|
|
246
|
-
|
|
247
|
-
columns.push({
|
|
248
|
-
name,
|
|
249
|
-
tableOid,
|
|
250
|
-
columnIndex,
|
|
251
|
-
typeOid,
|
|
252
|
-
typeLength,
|
|
253
|
-
typeModifier,
|
|
254
|
-
formatCode
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return columns;
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
// Parse DataRow message
|
|
262
|
-
export let parseDataRow = (body: Uint8Array): (string | null)[] => {
|
|
263
|
-
let view = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
264
|
-
let decoder = new TextDecoder();
|
|
265
|
-
let fieldCount = view.getInt16(0, false);
|
|
266
|
-
let values: (string | null)[] = [];
|
|
267
|
-
let offset = 2;
|
|
268
|
-
|
|
269
|
-
for (let i = 0; i < fieldCount; i++) {
|
|
270
|
-
let length = view.getInt32(offset, false);
|
|
271
|
-
offset += 4;
|
|
272
|
-
|
|
273
|
-
if (length === -1) {
|
|
274
|
-
values.push(null);
|
|
275
|
-
} else {
|
|
276
|
-
let value = decoder.decode(body.slice(offset, offset + length));
|
|
277
|
-
values.push(value);
|
|
278
|
-
offset += length;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return values;
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
// Parse CommandComplete message to extract affected row count
|
|
286
|
-
export let parseCommandComplete = (
|
|
287
|
-
body: Uint8Array
|
|
288
|
-
): { command: string; rowCount: number | null } => {
|
|
289
|
-
let decoder = new TextDecoder();
|
|
290
|
-
let tag = decoder.decode(body.slice(0, body.indexOf(0)));
|
|
291
|
-
let parts = tag.split(' ');
|
|
292
|
-
let command = parts[0] || tag;
|
|
293
|
-
let lastPart = parts[parts.length - 1];
|
|
294
|
-
let rowCount = lastPart && /^\d+$/.test(lastPart) ? parseInt(lastPart, 10) : null;
|
|
295
|
-
return { command, rowCount };
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
// Map PostgreSQL OIDs to human-readable type names
|
|
299
|
-
export let oidToTypeName = (oid: number): string => {
|
|
300
|
-
let typeMap: Record<number, string> = {
|
|
301
|
-
16: 'boolean',
|
|
302
|
-
17: 'bytea',
|
|
303
|
-
18: 'char',
|
|
304
|
-
19: 'name',
|
|
305
|
-
20: 'bigint',
|
|
306
|
-
21: 'smallint',
|
|
307
|
-
23: 'integer',
|
|
308
|
-
25: 'text',
|
|
309
|
-
26: 'oid',
|
|
310
|
-
114: 'json',
|
|
311
|
-
142: 'xml',
|
|
312
|
-
600: 'point',
|
|
313
|
-
700: 'real',
|
|
314
|
-
701: 'double precision',
|
|
315
|
-
790: 'money',
|
|
316
|
-
869: 'inet',
|
|
317
|
-
1042: 'character',
|
|
318
|
-
1043: 'varchar',
|
|
319
|
-
1082: 'date',
|
|
320
|
-
1083: 'time',
|
|
321
|
-
1114: 'timestamp',
|
|
322
|
-
1184: 'timestamptz',
|
|
323
|
-
1186: 'interval',
|
|
324
|
-
1266: 'timetz',
|
|
325
|
-
1700: 'numeric',
|
|
326
|
-
2950: 'uuid',
|
|
327
|
-
3802: 'jsonb',
|
|
328
|
-
3904: 'int4range',
|
|
329
|
-
3906: 'numrange',
|
|
330
|
-
3908: 'tsrange',
|
|
331
|
-
3910: 'tstzrange',
|
|
332
|
-
3912: 'daterange',
|
|
333
|
-
3926: 'int8range'
|
|
334
|
-
};
|
|
335
|
-
return typeMap[oid] || `oid:${oid}`;
|
|
336
|
-
};
|
package/src/spec.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { SlateSpecification } from '@slates/provider';
|
|
2
|
-
import { auth } from './auth';
|
|
3
|
-
import { config } from './config';
|
|
4
|
-
|
|
5
|
-
export let spec = SlateSpecification.create({
|
|
6
|
-
key: 'postgresql',
|
|
7
|
-
name: 'PostgreSQL',
|
|
8
|
-
description:
|
|
9
|
-
'Connect to PostgreSQL databases to query, insert, update, and delete data, manage schemas and tables, and monitor table changes.',
|
|
10
|
-
metadata: {},
|
|
11
|
-
config,
|
|
12
|
-
auth
|
|
13
|
-
});
|
package/src/tools/delete-rows.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { SlateTool } from '@slates/provider';
|
|
2
|
-
import { spec } from '../spec';
|
|
3
|
-
import { createClient, qualifiedTableName } from '../lib/helpers';
|
|
4
|
-
import { postgresServiceError } from '../lib/errors';
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
|
|
7
|
-
export let deleteRows = SlateTool.create(spec, {
|
|
8
|
-
name: 'Delete Rows',
|
|
9
|
-
key: 'delete_rows',
|
|
10
|
-
description: `Delete rows from a PostgreSQL table based on a WHERE condition.
|
|
11
|
-
Supports returning the deleted rows and requires explicit confirmation for full-table deletes.`,
|
|
12
|
-
instructions: [
|
|
13
|
-
'Always provide a WHERE condition to target specific rows.',
|
|
14
|
-
'Set confirmDeleteAll to true to delete all rows when no WHERE condition is provided.'
|
|
15
|
-
],
|
|
16
|
-
tags: {
|
|
17
|
-
destructive: true
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
.input(
|
|
21
|
-
z.object({
|
|
22
|
-
tableName: z.string().describe('Name of the table to delete from'),
|
|
23
|
-
schemaName: z.string().optional().describe('Schema containing the table'),
|
|
24
|
-
where: z
|
|
25
|
-
.string()
|
|
26
|
-
.optional()
|
|
27
|
-
.describe(
|
|
28
|
-
'SQL WHERE condition (without the WHERE keyword). Example: "id = 1 AND status = \'inactive\'"'
|
|
29
|
-
),
|
|
30
|
-
confirmDeleteAll: z
|
|
31
|
-
.boolean()
|
|
32
|
-
.optional()
|
|
33
|
-
.default(false)
|
|
34
|
-
.describe('Must be true to delete all rows when no WHERE condition is specified'),
|
|
35
|
-
returning: z
|
|
36
|
-
.boolean()
|
|
37
|
-
.optional()
|
|
38
|
-
.default(true)
|
|
39
|
-
.describe('Whether to return the deleted rows using RETURNING *')
|
|
40
|
-
})
|
|
41
|
-
)
|
|
42
|
-
.output(
|
|
43
|
-
z.object({
|
|
44
|
-
deletedCount: z.number().describe('Number of rows deleted'),
|
|
45
|
-
returnedRows: z
|
|
46
|
-
.array(z.record(z.string(), z.any()))
|
|
47
|
-
.describe('Deleted rows (if returning was enabled)')
|
|
48
|
-
})
|
|
49
|
-
)
|
|
50
|
-
.handleInvocation(async ctx => {
|
|
51
|
-
let client = createClient(ctx.auth, ctx.config);
|
|
52
|
-
let schema = ctx.input.schemaName || ctx.config.defaultSchema;
|
|
53
|
-
let fullTableName = qualifiedTableName(ctx.input.tableName, schema);
|
|
54
|
-
|
|
55
|
-
if (!ctx.input.where && !ctx.input.confirmDeleteAll) {
|
|
56
|
-
throw postgresServiceError(
|
|
57
|
-
'No WHERE condition specified. Set confirmDeleteAll to true to delete all rows from the table.'
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let sql = `DELETE FROM ${fullTableName}`;
|
|
62
|
-
|
|
63
|
-
if (ctx.input.where) {
|
|
64
|
-
sql += ` WHERE ${ctx.input.where}`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (ctx.input.returning) {
|
|
68
|
-
sql += ' RETURNING *';
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
ctx.info(
|
|
72
|
-
`Deleting rows from ${fullTableName}${ctx.input.where ? ` where ${ctx.input.where}` : ' (all rows)'}`
|
|
73
|
-
);
|
|
74
|
-
let result = await client.query(sql, ctx.config.queryTimeout);
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
output: {
|
|
78
|
-
deletedCount: result.rowCount ?? 0,
|
|
79
|
-
returnedRows: result.rows
|
|
80
|
-
},
|
|
81
|
-
message: `Deleted **${result.rowCount ?? 0}** row(s) from \`${ctx.input.tableName}\`.`
|
|
82
|
-
};
|
|
83
|
-
})
|
|
84
|
-
.build();
|