@eamonboyle/mssql-mcp 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +379 -361
- package/dist/apps/queryResultsApp.d.ts +18 -0
- package/dist/apps/queryResultsApp.d.ts.map +1 -0
- package/dist/apps/queryResultsApp.js +272 -0
- package/dist/apps/queryResultsApp.js.map +1 -0
- package/dist/checkNodeVersion.d.ts +2 -0
- package/dist/checkNodeVersion.d.ts.map +1 -0
- package/dist/checkNodeVersion.js +9 -0
- package/dist/checkNodeVersion.js.map +1 -0
- package/dist/config.d.ts +58 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +202 -33
- package/dist/config.js.map +1 -1
- package/dist/confirmationMessage.d.ts +2 -0
- package/dist/confirmationMessage.d.ts.map +1 -0
- package/dist/confirmationMessage.js +105 -0
- package/dist/confirmationMessage.js.map +1 -0
- package/dist/db.d.ts +24 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +152 -36
- package/dist/db.js.map +1 -1
- package/dist/filteredRead.js +0 -0
- package/dist/httpServer.d.ts +25 -0
- package/dist/httpServer.d.ts.map +1 -0
- package/dist/httpServer.js +128 -0
- package/dist/httpServer.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -366
- package/dist/index.js.map +1 -1
- package/dist/mcpResults.d.ts +1 -1
- package/dist/mcpResults.d.ts.map +1 -1
- package/dist/mcpResults.js +2 -2
- package/dist/mcpResults.js.map +1 -1
- package/dist/nodeVersion.d.ts +14 -0
- package/dist/nodeVersion.d.ts.map +1 -0
- package/dist/nodeVersion.js +23 -0
- package/dist/nodeVersion.js.map +1 -0
- package/dist/packageInfo.d.ts +3 -0
- package/dist/packageInfo.d.ts.map +1 -0
- package/dist/packageInfo.js +5 -0
- package/dist/packageInfo.js.map +1 -0
- package/dist/promptRegistry.d.ts +1 -1
- package/dist/promptRegistry.d.ts.map +1 -1
- package/dist/promptRegistry.js +1 -1
- package/dist/promptRegistry.js.map +1 -1
- package/dist/resourceRegistry.d.ts +9 -1
- package/dist/resourceRegistry.d.ts.map +1 -1
- package/dist/resourceRegistry.js +9 -2
- package/dist/resourceRegistry.js.map +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +16 -20
- package/dist/schema.js.map +1 -1
- package/dist/server.d.ts +5 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +320 -0
- package/dist/server.js.map +1 -0
- package/dist/serverState.js +2 -4
- package/dist/serverState.js.map +1 -1
- package/dist/sql.js +0 -0
- package/dist/sqlErrors.d.ts +10 -0
- package/dist/sqlErrors.d.ts.map +1 -0
- package/dist/sqlErrors.js +32 -0
- package/dist/sqlErrors.js.map +1 -0
- package/dist/toolRegistry.d.ts +1 -1
- package/dist/toolRegistry.d.ts.map +1 -1
- package/dist/toolRegistry.js +1 -1
- package/dist/toolRegistry.js.map +1 -1
- package/dist/tools/AnalyzeTableTool.js +2 -4
- package/dist/tools/AnalyzeTableTool.js.map +1 -1
- package/dist/tools/CreateIndexTool.js +2 -4
- package/dist/tools/CreateIndexTool.js.map +1 -1
- package/dist/tools/CreateTableTool.js +2 -4
- package/dist/tools/CreateTableTool.js.map +1 -1
- package/dist/tools/DeleteDataTool.js +2 -4
- package/dist/tools/DeleteDataTool.js.map +1 -1
- package/dist/tools/DescribeDependenciesTool.js +2 -4
- package/dist/tools/DescribeDependenciesTool.js.map +1 -1
- package/dist/tools/DescribeObjectTool.js +2 -4
- package/dist/tools/DescribeObjectTool.js.map +1 -1
- package/dist/tools/DescribeRelationshipsTool.js +2 -4
- package/dist/tools/DescribeRelationshipsTool.js.map +1 -1
- package/dist/tools/DescribeTableTool.js +2 -4
- package/dist/tools/DescribeTableTool.js.map +1 -1
- package/dist/tools/DropTableTool.js +2 -4
- package/dist/tools/DropTableTool.js.map +1 -1
- package/dist/tools/ExplainQueryTool.d.ts.map +1 -1
- package/dist/tools/ExplainQueryTool.js +4 -6
- package/dist/tools/ExplainQueryTool.js.map +1 -1
- package/dist/tools/InsertDataTool.js +2 -4
- package/dist/tools/InsertDataTool.js.map +1 -1
- package/dist/tools/ListDatabasesTool.d.ts.map +1 -1
- package/dist/tools/ListDatabasesTool.js +2 -4
- package/dist/tools/ListDatabasesTool.js.map +1 -1
- package/dist/tools/ListForeignKeysTool.js +2 -4
- package/dist/tools/ListForeignKeysTool.js.map +1 -1
- package/dist/tools/ListLargestTablesTool.js +2 -4
- package/dist/tools/ListLargestTablesTool.js.map +1 -1
- package/dist/tools/ListObjectsTool.js +2 -4
- package/dist/tools/ListObjectsTool.js.map +1 -1
- package/dist/tools/ListTableTool.js +2 -4
- package/dist/tools/ListTableTool.js.map +1 -1
- package/dist/tools/PreviewDeleteTool.js +2 -4
- package/dist/tools/PreviewDeleteTool.js.map +1 -1
- package/dist/tools/PreviewUpdateTool.js +2 -4
- package/dist/tools/PreviewUpdateTool.js.map +1 -1
- package/dist/tools/ReadDataTool.d.ts +15 -12
- package/dist/tools/ReadDataTool.d.ts.map +1 -1
- package/dist/tools/ReadDataTool.js +86 -64
- package/dist/tools/ReadDataTool.js.map +1 -1
- package/dist/tools/SearchDataTool.js +2 -4
- package/dist/tools/SearchDataTool.js.map +1 -1
- package/dist/tools/SummarizeSchemaTool.js +2 -4
- package/dist/tools/SummarizeSchemaTool.js.map +1 -1
- package/dist/tools/UpdateDataTool.js +2 -4
- package/dist/tools/UpdateDataTool.js.map +1 -1
- package/dist/validation.d.ts +26 -3
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +237 -85
- package/dist/validation.js.map +1 -1
- package/dist/writePreview.js +0 -0
- package/dist/writePreviewGrant.js +0 -0
- package/dist/writePreviewGrantStore.js +0 -0
- package/dist/writeSafety.js +0 -0
- package/package.json +27 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListTableTool.js","sourceRoot":"","sources":["../../src/tools/ListTableTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAOlD,MAAM,OAAO,aAAa;
|
|
1
|
+
{"version":3,"file":"ListTableTool.js","sourceRoot":"","sources":["../../src/tools/ListTableTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAOlD,MAAM,OAAO,aAAa;IACxB,IAAI,GAAG,YAAY,CAAC;IACpB,WAAW,GACT,uEAAuE,CAAC;IAE1E,KAAK,CAAC,GAAG,CAAC,SAA0B,EAAE;QACpC,IAAI,CAAC;YACH,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;YAC5C,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;gBAC3C,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC;gBAClE,CAAC,CAAC,EAAE,CAAC;YAEP,MAAM,MAAM,GACV,WAAW,CAAC,MAAM,GAAG,CAAC;gBACpB,CAAC,CAAC,CACE,MAAM,OAAO,CAAC,GAAG,CACf,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAC7B,kBAAkB,CAAC,YAAY,EAAE,UAAU,CAAC,CAC7C,CACF,CACF,CAAC,IAAI,EAAE;gBACV,CAAC,CAAC,MAAM,kBAAkB,CAAC,YAAY,CAAC,CAAC;YAE7C,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,mCAAmC;gBAC5C,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBAC5B,IAAI,EAAE,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,EAAE;iBAC/C,CAAC,CAAC;aACJ,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;YAC9C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,0BAA0B,KAAK,EAAE;aAC3C,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { clampRowLimit } from "../config.js";
|
|
2
2
|
import { previewFilteredRows } from "../writePreview.js";
|
|
3
3
|
export class PreviewDeleteTool {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
this.description = "Previews which rows would be deleted before running delete_data.";
|
|
7
|
-
}
|
|
4
|
+
name = "preview_delete";
|
|
5
|
+
description = "Previews which rows would be deleted before running delete_data.";
|
|
8
6
|
async run(params) {
|
|
9
7
|
try {
|
|
10
8
|
const preview = await previewFilteredRows({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PreviewDeleteTool.js","sourceRoot":"","sources":["../../src/tools/PreviewDeleteTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAWzD,MAAM,OAAO,iBAAiB;
|
|
1
|
+
{"version":3,"file":"PreviewDeleteTool.js","sourceRoot":"","sources":["../../src/tools/PreviewDeleteTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAWzD,MAAM,OAAO,iBAAiB;IAC5B,IAAI,GAAG,gBAAgB,CAAC;IACxB,WAAW,GACT,kEAAkE,CAAC;IAErE,KAAK,CAAC,GAAG,CAAC,MAA2B;QACnC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;gBACxC,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,CAAC;aAC9C,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,aAAa,OAAO,CAAC,IAAI,CAAC,MAAM,uBAAuB,OAAO,CAAC,gBAAgB,4BAA4B;gBACpH,IAAI,EAAE,OAAO;aACd,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,6BAA6B,KAAK,EAAE;aAC9C,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { clampRowLimit } from "../config.js";
|
|
2
2
|
import { previewFilteredRows } from "../writePreview.js";
|
|
3
3
|
export class PreviewUpdateTool {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
this.description = "Previews which rows would be updated before running update_data.";
|
|
7
|
-
}
|
|
4
|
+
name = "preview_update";
|
|
5
|
+
description = "Previews which rows would be updated before running update_data.";
|
|
8
6
|
async run(params) {
|
|
9
7
|
try {
|
|
10
8
|
const preview = await previewFilteredRows({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PreviewUpdateTool.js","sourceRoot":"","sources":["../../src/tools/PreviewUpdateTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAYzD,MAAM,OAAO,iBAAiB;
|
|
1
|
+
{"version":3,"file":"PreviewUpdateTool.js","sourceRoot":"","sources":["../../src/tools/PreviewUpdateTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAYzD,MAAM,OAAO,iBAAiB;IAC5B,IAAI,GAAG,gBAAgB,CAAC;IACxB,WAAW,GACT,kEAAkE,CAAC;IAErE,KAAK,CAAC,GAAG,CAAC,MAA2B;QACnC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;gBACxC,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,CAAC;aAC9C,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,aAAa,OAAO,CAAC,IAAI,CAAC,MAAM,uBAAuB,OAAO,CAAC,gBAAgB,4BAA4B;gBACpH,IAAI,EAAE;oBACJ,GAAG,OAAO;oBACV,OAAO,EAAE,MAAM,CAAC,OAAO;iBACxB;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,6BAA6B,KAAK,EAAE;aAC9C,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,34 +1,37 @@
|
|
|
1
|
+
import sql from "mssql";
|
|
1
2
|
interface ReadDataParams {
|
|
2
3
|
query: string;
|
|
3
4
|
databaseName?: string;
|
|
4
5
|
}
|
|
6
|
+
interface StreamedRows {
|
|
7
|
+
rows: Record<string, unknown>[];
|
|
8
|
+
truncated: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Streams a query and stops reading once `maxRows` rows have arrived, so a
|
|
12
|
+
* large result is never fully buffered in memory.
|
|
13
|
+
*/
|
|
14
|
+
export declare function streamRows(request: sql.Request, query: string, maxRows: number): Promise<StreamedRows>;
|
|
5
15
|
export declare class ReadDataTool {
|
|
6
16
|
name: string;
|
|
7
17
|
description: string;
|
|
8
18
|
/**
|
|
9
|
-
*
|
|
10
|
-
* @param data The query result data
|
|
11
|
-
* @returns Sanitized data
|
|
12
|
-
*/
|
|
13
|
-
private sanitizeResult;
|
|
14
|
-
/**
|
|
15
|
-
* Executes the validated SQL query
|
|
16
|
-
* @param params Query parameters
|
|
17
|
-
* @returns Query execution result
|
|
19
|
+
* Removes unexpected characters from column names in the result.
|
|
18
20
|
*/
|
|
21
|
+
private sanitizeColumnNames;
|
|
19
22
|
run(params: ReadDataParams): Promise<{
|
|
20
23
|
success: boolean;
|
|
21
24
|
message: string;
|
|
22
25
|
error: string;
|
|
23
26
|
data?: undefined;
|
|
24
27
|
recordCount?: undefined;
|
|
25
|
-
|
|
28
|
+
truncated?: undefined;
|
|
26
29
|
} | {
|
|
27
30
|
success: boolean;
|
|
28
31
|
message: string;
|
|
29
|
-
data: unknown[];
|
|
32
|
+
data: Record<string, unknown>[];
|
|
30
33
|
recordCount: number;
|
|
31
|
-
|
|
34
|
+
truncated: boolean;
|
|
32
35
|
error?: undefined;
|
|
33
36
|
}>;
|
|
34
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadDataTool.d.ts","sourceRoot":"","sources":["../../src/tools/ReadDataTool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ReadDataTool.d.ts","sourceRoot":"","sources":["../../src/tools/ReadDataTool.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,OAAO,CAAC;AAMxB,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,GAAG,CAAC,OAAO,EACpB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CAkCvB;AAED,qBAAa,YAAY;IACvB,IAAI,SAAe;IACnB,WAAW,SACuL;IAElM;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAgBrB,GAAG,CAAC,MAAM,EAAE,cAAc;;;;;;;;;;;;;;;CA4DjC"}
|
|
@@ -1,93 +1,115 @@
|
|
|
1
|
+
import sql from "mssql";
|
|
1
2
|
import { getMaxRows } from "../config.js";
|
|
2
|
-
import {
|
|
3
|
+
import { bindRequestCancellation, getSqlPool } from "../db.js";
|
|
4
|
+
import { describeSqlError, isCancellation } from "../sqlErrors.js";
|
|
3
5
|
import { validateReadQuery } from "../validation.js";
|
|
6
|
+
/**
|
|
7
|
+
* Streams a query and stops reading once `maxRows` rows have arrived, so a
|
|
8
|
+
* large result is never fully buffered in memory.
|
|
9
|
+
*/
|
|
10
|
+
export function streamRows(request, query, maxRows) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const rows = [];
|
|
13
|
+
let truncated = false;
|
|
14
|
+
let failure;
|
|
15
|
+
request.stream = true;
|
|
16
|
+
request.on("row", (row) => {
|
|
17
|
+
if (rows.length < maxRows) {
|
|
18
|
+
rows.push(row);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!truncated) {
|
|
22
|
+
// One row past the limit proves there is more data; stop the server.
|
|
23
|
+
truncated = true;
|
|
24
|
+
request.cancel();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
request.on("error", (error) => {
|
|
28
|
+
// Our own cancel surfaces as ECANCEL; the rows read so far are valid.
|
|
29
|
+
if (truncated && error?.code === "ECANCEL") {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
failure ??= error;
|
|
33
|
+
});
|
|
34
|
+
// Settle only on "done": until then the request still owns the
|
|
35
|
+
// connection, and rolling back the transaction would fail and leak it.
|
|
36
|
+
request.on("done", () => failure ? reject(failure) : resolve({ rows, truncated }));
|
|
37
|
+
// Callback form: in stream mode errors arrive via the "error" event, and
|
|
38
|
+
// the promise form would otherwise reject unobserved.
|
|
39
|
+
request.query(query, () => undefined);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
4
42
|
export class ReadDataTool {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.description = "Executes a SELECT query on an MSSQL Database table. The query must start with SELECT and cannot contain any destructive SQL operations for security reasons.";
|
|
8
|
-
}
|
|
43
|
+
name = "read_data";
|
|
44
|
+
description = "Executes a read-only SELECT query (CTEs with WITH are allowed) on an MSSQL database. Only a single SELECT statement is accepted, and it runs inside a transaction that is always rolled back.";
|
|
9
45
|
/**
|
|
10
|
-
*
|
|
11
|
-
* @param data The query result data
|
|
12
|
-
* @returns Sanitized data
|
|
46
|
+
* Removes unexpected characters from column names in the result.
|
|
13
47
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
console.warn(`Query returned ${data.length} records, limiting to ${maxRecords}`);
|
|
22
|
-
return data.slice(0, maxRecords);
|
|
23
|
-
}
|
|
24
|
-
return data.map((record) => {
|
|
25
|
-
if (typeof record === "object" && record !== null) {
|
|
26
|
-
const sanitized = {};
|
|
27
|
-
for (const [key, value] of Object.entries(record)) {
|
|
28
|
-
// Sanitize column names (remove any suspicious characters)
|
|
29
|
-
const sanitizedKey = key.replace(/[^\w\s-_.]/g, "");
|
|
30
|
-
if (sanitizedKey !== key) {
|
|
31
|
-
console.warn(`Column name sanitized: ${key} -> ${sanitizedKey}`);
|
|
32
|
-
}
|
|
33
|
-
sanitized[sanitizedKey] = value;
|
|
48
|
+
sanitizeColumnNames(rows) {
|
|
49
|
+
return rows.map((record) => {
|
|
50
|
+
const sanitized = {};
|
|
51
|
+
for (const [key, value] of Object.entries(record)) {
|
|
52
|
+
const sanitizedKey = key.replace(/[^\w\s-_.]/g, "");
|
|
53
|
+
if (sanitizedKey !== key) {
|
|
54
|
+
console.error(`Column name sanitized: ${key} -> ${sanitizedKey}`);
|
|
34
55
|
}
|
|
35
|
-
|
|
56
|
+
sanitized[sanitizedKey] = value;
|
|
36
57
|
}
|
|
37
|
-
return
|
|
58
|
+
return sanitized;
|
|
38
59
|
});
|
|
39
60
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Executes the validated SQL query
|
|
42
|
-
* @param params Query parameters
|
|
43
|
-
* @returns Query execution result
|
|
44
|
-
*/
|
|
45
61
|
async run(params) {
|
|
62
|
+
const { query, databaseName } = params;
|
|
63
|
+
const validation = validateReadQuery(query);
|
|
64
|
+
if (!validation.isValid) {
|
|
65
|
+
console.error(`Security validation failed for query: ${query?.substring?.(0, 100)}...`);
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
message: `Security validation failed: ${validation.error}`,
|
|
69
|
+
error: "SECURITY_VALIDATION_FAILED",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
let transaction;
|
|
46
73
|
try {
|
|
47
|
-
const {
|
|
48
|
-
const { request, error } = await getSqlRequest(databaseName);
|
|
74
|
+
const { pool, error } = await getSqlPool(databaseName);
|
|
49
75
|
if (error) {
|
|
50
76
|
return { success: false, message: error, error: "INVALID_DATABASE" };
|
|
51
77
|
}
|
|
52
|
-
// Validate the query for security issues
|
|
53
|
-
const validation = validateReadQuery(query);
|
|
54
|
-
if (!validation.isValid) {
|
|
55
|
-
console.warn(`Security validation failed for query: ${query.substring(0, 100)}...`);
|
|
56
|
-
return {
|
|
57
|
-
success: false,
|
|
58
|
-
message: `Security validation failed: ${validation.error}`,
|
|
59
|
-
error: "SECURITY_VALIDATION_FAILED",
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
78
|
// Audit on stderr only — stdout must stay JSON-RPC for MCP stdio transport.
|
|
63
79
|
console.error(`Executing validated SELECT query: ${query.substring(0, 200)}${query.length > 200 ? "..." : ""}`);
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
80
|
+
// Defense in depth: anything that slipped past validation is undone.
|
|
81
|
+
transaction = new sql.Transaction(pool);
|
|
82
|
+
await transaction.begin();
|
|
83
|
+
const request = bindRequestCancellation(new sql.Request(transaction));
|
|
84
|
+
const maxRows = getMaxRows();
|
|
85
|
+
const { rows, truncated } = await streamRows(request, query, maxRows);
|
|
86
|
+
const data = this.sanitizeColumnNames(rows);
|
|
68
87
|
return {
|
|
69
88
|
success: true,
|
|
70
|
-
message:
|
|
71
|
-
? `
|
|
72
|
-
:
|
|
73
|
-
data
|
|
74
|
-
recordCount:
|
|
75
|
-
|
|
89
|
+
message: truncated
|
|
90
|
+
? `Query executed successfully. Returned the first ${data.length} record(s); more rows exist beyond MAX_ROWS (${maxRows}). Add TOP, WHERE, or OFFSET/FETCH to narrow the result.`
|
|
91
|
+
: `Query executed successfully. Retrieved ${data.length} record(s)`,
|
|
92
|
+
data,
|
|
93
|
+
recordCount: data.length,
|
|
94
|
+
truncated,
|
|
76
95
|
};
|
|
77
96
|
}
|
|
78
97
|
catch (error) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
if (isCancellation(error)) {
|
|
99
|
+
console.error("Query canceled by the client.");
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.error("Error executing query:", error);
|
|
103
|
+
}
|
|
85
104
|
return {
|
|
86
105
|
success: false,
|
|
87
|
-
message: `Failed to execute query: ${
|
|
106
|
+
message: `Failed to execute query: ${describeSqlError(error)}`,
|
|
88
107
|
error: "QUERY_EXECUTION_FAILED",
|
|
89
108
|
};
|
|
90
109
|
}
|
|
110
|
+
finally {
|
|
111
|
+
await transaction?.rollback().catch(() => undefined);
|
|
112
|
+
}
|
|
91
113
|
}
|
|
92
114
|
}
|
|
93
115
|
//# sourceMappingURL=ReadDataTool.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadDataTool.js","sourceRoot":"","sources":["../../src/tools/ReadDataTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ReadDataTool.js","sourceRoot":"","sources":["../../src/tools/ReadDataTool.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,8 @@ import { clampRowLimit, getDefaultSearchLimit } from "../config.js";
|
|
|
2
2
|
import { getSqlRequest } from "../db.js";
|
|
3
3
|
import { buildQualifiedName, quoteIdentifier } from "../sql.js";
|
|
4
4
|
export class SearchDataTool {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.description = "Searches rows in a table by applying a parameterized LIKE filter across one or more columns.";
|
|
8
|
-
}
|
|
5
|
+
name = "search_data";
|
|
6
|
+
description = "Searches rows in a table by applying a parameterized LIKE filter across one or more columns.";
|
|
9
7
|
async run(params) {
|
|
10
8
|
try {
|
|
11
9
|
const { tableName, schemaName, columns, searchTerm, limit, databaseName, } = params;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchDataTool.js","sourceRoot":"","sources":["../../src/tools/SearchDataTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAWhE,MAAM,OAAO,cAAc;
|
|
1
|
+
{"version":3,"file":"SearchDataTool.js","sourceRoot":"","sources":["../../src/tools/SearchDataTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAWhE,MAAM,OAAO,cAAc;IACzB,IAAI,GAAG,aAAa,CAAC;IACrB,WAAW,GACT,8FAA8F,CAAC;IAEjG,KAAK,CAAC,GAAG,CAAC,MAAwB;QAChC,IAAI,CAAC;YACH,MAAM,EACJ,SAAS,EACT,UAAU,EACV,OAAO,EACP,UAAU,EACV,KAAK,EACL,YAAY,GACb,GAAG,MAAM,CAAC;YAEX,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACpD,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,OAAO,EAAE,gDAAgD;iBAC1D,CAAC;YACJ,CAAC;YAED,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBAC/D,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,OAAO,EAAE,oDAAoD;iBAC9D,CAAC;YACJ,CAAC;YAED,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;YAC7D,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YAC5C,CAAC;YAED,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;YACrE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAc,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7E,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;YAC1D,MAAM,WAAW,GAAG,WAAW;iBAC5B,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE;gBACzB,MAAM,aAAa,GAAG,UAAU,KAAK,EAAE,CAAC;gBACxC,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,IAAI,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBACvD,OAAO,GAAG,UAAU,UAAU,aAAa,EAAE,CAAC;YAChD,CAAC,CAAC;iBACD,IAAI,CAAC,MAAM,CAAC,CAAC;YAEhB,MAAM,KAAK,GAAG;sBACE,GAAG;eACV,kBAAkB;gBACjB,WAAW;OACpB,CAAC;YACF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAE1C,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,4CAA4C,MAAM,CAAC,SAAS,CAAC,MAAM,UAAU;gBACtF,IAAI,EAAE,MAAM,CAAC,SAAS;gBACtB,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM;gBACpC,KAAK,EAAE,GAAG;aACX,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;YAC9C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,0BAA0B,KAAK,EAAE;aAC3C,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -10,10 +10,8 @@ export function formatSchemaSummaryText(data) {
|
|
|
10
10
|
return `Schema summary: ${typeSummary || "no typed objects"}.${schemaSummary ? ` Top schemas: ${schemaSummary}.` : ""}`;
|
|
11
11
|
}
|
|
12
12
|
export class SummarizeSchemaTool {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.description = "Returns a high-level schema summary for a database: object counts by type and object counts per schema.";
|
|
16
|
-
}
|
|
13
|
+
name = "summarize_schema";
|
|
14
|
+
description = "Returns a high-level schema summary for a database: object counts by type and object counts per schema.";
|
|
17
15
|
async run(params = {}) {
|
|
18
16
|
try {
|
|
19
17
|
const data = await getDatabaseSchemaSummary(params.databaseName);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SummarizeSchemaTool.js","sourceRoot":"","sources":["../../src/tools/SummarizeSchemaTool.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,GAEzB,MAAM,cAAc,CAAC;AAMtB,MAAM,UAAU,uBAAuB,CAAC,IAAyB;IAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY;SAClC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,UAAU,KAAK,GAAG,CAAC,WAAW,EAAE,CAAC;SACrD,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY;SACpC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,UAAU,KAAK,GAAG,CAAC,WAAW,EAAE,CAAC;SACrD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO,mBAAmB,WAAW,IAAI,kBAAkB,IACzD,aAAa,CAAC,CAAC,CAAC,iBAAiB,aAAa,GAAG,CAAC,CAAC,CAAC,EACtD,EAAE,CAAC;AACL,CAAC;AAED,MAAM,OAAO,mBAAmB;
|
|
1
|
+
{"version":3,"file":"SummarizeSchemaTool.js","sourceRoot":"","sources":["../../src/tools/SummarizeSchemaTool.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,GAEzB,MAAM,cAAc,CAAC;AAMtB,MAAM,UAAU,uBAAuB,CAAC,IAAyB;IAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY;SAClC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,UAAU,KAAK,GAAG,CAAC,WAAW,EAAE,CAAC;SACrD,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY;SACpC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,UAAU,KAAK,GAAG,CAAC,WAAW,EAAE,CAAC;SACrD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO,mBAAmB,WAAW,IAAI,kBAAkB,IACzD,aAAa,CAAC,CAAC,CAAC,iBAAiB,aAAa,GAAG,CAAC,CAAC,CAAC,EACtD,EAAE,CAAC;AACL,CAAC;AAED,MAAM,OAAO,mBAAmB;IAC9B,IAAI,GAAG,kBAAkB,CAAC;IAC1B,WAAW,GACT,yGAAyG,CAAC;IAE5G,KAAK,CAAC,GAAG,CAAC,SAAgC,EAAE;QAC1C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,wBAAwB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YAEjE,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACrE,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,OAAO,EACL,gJAAgJ;oBAClJ,IAAI;iBACL,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,uBAAuB,CAAC,IAAI,CAAC;gBACtC,IAAI;aACL,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,+BAA+B,KAAK,EAAE;aAChD,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -3,10 +3,8 @@ import { getSqlRequest } from "../db.js";
|
|
|
3
3
|
import { buildQualifiedName, quoteIdentifier } from "../sql.js";
|
|
4
4
|
import { buildParameterizedWhereClause } from "../writeSafety.js";
|
|
5
5
|
export class UpdateDataTool {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
this.description = "Updates data in an MSSQL Database table using required structured filters.";
|
|
9
|
-
}
|
|
6
|
+
name = "update_data";
|
|
7
|
+
description = "Updates data in an MSSQL Database table using required structured filters.";
|
|
10
8
|
async run(params) {
|
|
11
9
|
let query;
|
|
12
10
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpdateDataTool.js","sourceRoot":"","sources":["../../src/tools/UpdateDataTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,6BAA6B,EAAkB,MAAM,mBAAmB,CAAC;AAUlF,MAAM,OAAO,cAAc;
|
|
1
|
+
{"version":3,"file":"UpdateDataTool.js","sourceRoot":"","sources":["../../src/tools/UpdateDataTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,6BAA6B,EAAkB,MAAM,mBAAmB,CAAC;AAUlF,MAAM,OAAO,cAAc;IACzB,IAAI,GAAG,aAAa,CAAC;IACrB,WAAW,GACT,4EAA4E,CAAC;IAE/E,KAAK,CAAC,GAAG,CAAC,MAAwB;QAChC,IAAI,KAAyB,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;YAEzE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;YAC7D,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YAC5C,CAAC;YAED,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3C,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC7D,CAAC;YAED,MAAM,SAAS,GAAG,aAAa;iBAC5B,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;gBAClB,MAAM,SAAS,GAAG,UAAU,KAAK,EAAE,CAAC;gBACpC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;gBACvC,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,SAAS,EAAE,CAAC;YACnD,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;YAEd,MAAM,WAAW,GAAG,6BAA6B,CAC/C,OAAO,EACP,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAC3C,eAAe,CAChB,CAAC;YAEF,KAAK,GAAG,UAAU,kBAAkB,CAAC,SAAS,EAAE,UAAU,CAAC,QAAQ,SAAS,UAAU,WAAW,EAAE,CAAC;YACpG,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACrC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAChC,8BAA8B,KAAK,mBAAmB,CACvD,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,kCAAkC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,kBAAkB;gBACnF,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;aACrC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,yBAAyB,KAAK,CAAC,CAAC,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,EAAE;aAC9E,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
|
package/dist/validation.d.ts
CHANGED
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SQL query validation for read-only operations.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* The query is tokenized following T-SQL lexing rules (string literals,
|
|
5
|
+
* quoted identifiers, nested comments, numeric/hex/money literals, CR or LF
|
|
6
|
+
* line endings) so checks compare whole tokens. This matters because SQL
|
|
7
|
+
* Server splits `1COMMIT` into `1` and `COMMIT` and runs consecutive
|
|
8
|
+
* statements without a separating semicolon; regex word boundaries miss both.
|
|
9
|
+
*
|
|
10
|
+
* Validation is one layer: read_data also runs every query inside a
|
|
11
|
+
* transaction that is always rolled back, and a least-privilege login remains
|
|
12
|
+
* the primary control.
|
|
4
13
|
*/
|
|
14
|
+
type TokenType = "word" | "number" | "string" | "identifier" | "punct";
|
|
15
|
+
export interface SqlToken {
|
|
16
|
+
type: TokenType;
|
|
17
|
+
/** Uppercased text for words; raw text otherwise. */
|
|
18
|
+
value: string;
|
|
19
|
+
}
|
|
5
20
|
export interface ValidationResult {
|
|
6
21
|
isValid: boolean;
|
|
7
22
|
error?: string;
|
|
8
23
|
}
|
|
9
24
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
25
|
+
* Splits a query into T-SQL tokens. Comments and whitespace are dropped.
|
|
26
|
+
* Returns null when a string literal, quoted identifier, or block comment is
|
|
27
|
+
* left unterminated.
|
|
28
|
+
*/
|
|
29
|
+
export declare function tokenizeSql(query: string): SqlToken[] | null;
|
|
30
|
+
/**
|
|
31
|
+
* Validates a SQL query for read-only use: exactly one SELECT statement,
|
|
32
|
+
* optionally preceded by common table expressions and combined with
|
|
33
|
+
* UNION/EXCEPT/INTERSECT.
|
|
12
34
|
*/
|
|
13
35
|
export declare function validateReadQuery(query: string): ValidationResult;
|
|
36
|
+
export {};
|
|
14
37
|
//# sourceMappingURL=validation.d.ts.map
|
package/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAqEH,KAAK,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,OAAO,CAAC;AAEvE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,SAAS,CAAC;IAChB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAWD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CA2H5D;AASD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAuFjE"}
|