@berthojoris/mcp-mysql-server 1.2.6 → 1.4.1
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/CHANGELOG.md +75 -0
- package/README.md +324 -1
- package/dist/config/featureConfig.d.ts +5 -0
- package/dist/config/featureConfig.js +59 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +18 -1
- package/dist/mcp-server.js +121 -27
- package/dist/tools/dataExportTools.d.ts +33 -0
- package/dist/tools/dataExportTools.js +240 -0
- package/dist/validation/schemas.d.ts +87 -0
- package/dist/validation/schemas.js +62 -2
- package/manifest.json +247 -247
- package/package.json +3 -2
package/dist/mcp-server.js
CHANGED
|
@@ -692,11 +692,85 @@ const TOOLS = [
|
|
|
692
692
|
required: ['procedure_name'],
|
|
693
693
|
},
|
|
694
694
|
},
|
|
695
|
+
// Data Export Tools
|
|
696
|
+
{
|
|
697
|
+
name: 'export_table_to_csv',
|
|
698
|
+
description: 'Export table data to CSV format with optional filtering, pagination, and sorting.',
|
|
699
|
+
inputSchema: {
|
|
700
|
+
type: 'object',
|
|
701
|
+
properties: {
|
|
702
|
+
table_name: {
|
|
703
|
+
type: 'string',
|
|
704
|
+
description: 'Name of the table to export',
|
|
705
|
+
},
|
|
706
|
+
filters: {
|
|
707
|
+
type: 'array',
|
|
708
|
+
description: 'Array of filter conditions',
|
|
709
|
+
items: {
|
|
710
|
+
type: 'object',
|
|
711
|
+
properties: {
|
|
712
|
+
field: { type: 'string' },
|
|
713
|
+
operator: {
|
|
714
|
+
type: 'string',
|
|
715
|
+
enum: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'like', 'in']
|
|
716
|
+
},
|
|
717
|
+
value: {
|
|
718
|
+
description: 'Value to compare against (can be string, number, boolean, or array for "in" operator)'
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
required: ['field', 'operator', 'value'],
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
pagination: {
|
|
725
|
+
type: 'object',
|
|
726
|
+
properties: {
|
|
727
|
+
page: { type: 'number', description: 'Page number (starting from 1)' },
|
|
728
|
+
limit: { type: 'number', description: 'Number of records per page' },
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
sorting: {
|
|
732
|
+
type: 'object',
|
|
733
|
+
properties: {
|
|
734
|
+
field: { type: 'string', description: 'Field name to sort by' },
|
|
735
|
+
direction: { type: 'string', enum: ['asc', 'desc'] },
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
include_headers: {
|
|
739
|
+
type: 'boolean',
|
|
740
|
+
description: 'Whether to include column headers in the CSV output',
|
|
741
|
+
},
|
|
742
|
+
},
|
|
743
|
+
required: ['table_name'],
|
|
744
|
+
},
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
name: 'export_query_to_csv',
|
|
748
|
+
description: 'Export the results of a SELECT query to CSV format.',
|
|
749
|
+
inputSchema: {
|
|
750
|
+
type: 'object',
|
|
751
|
+
properties: {
|
|
752
|
+
query: {
|
|
753
|
+
type: 'string',
|
|
754
|
+
description: 'SQL SELECT query to execute and export',
|
|
755
|
+
},
|
|
756
|
+
params: {
|
|
757
|
+
type: 'array',
|
|
758
|
+
description: 'Optional array of parameters for parameterized queries',
|
|
759
|
+
items: {},
|
|
760
|
+
},
|
|
761
|
+
include_headers: {
|
|
762
|
+
type: 'boolean',
|
|
763
|
+
description: 'Whether to include column headers in the CSV output',
|
|
764
|
+
},
|
|
765
|
+
},
|
|
766
|
+
required: ['query'],
|
|
767
|
+
},
|
|
768
|
+
},
|
|
695
769
|
];
|
|
696
770
|
// Create the MCP server
|
|
697
771
|
const server = new index_js_1.Server({
|
|
698
772
|
name: 'mysql-mcp-server',
|
|
699
|
-
version: '1.
|
|
773
|
+
version: '1.4.1',
|
|
700
774
|
}, {
|
|
701
775
|
capabilities: {
|
|
702
776
|
tools: {},
|
|
@@ -718,49 +792,49 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
718
792
|
result = await mysqlMCP.listDatabases();
|
|
719
793
|
break;
|
|
720
794
|
case 'list_tables':
|
|
721
|
-
result = await mysqlMCP.listTables(args);
|
|
795
|
+
result = await mysqlMCP.listTables((args || {}));
|
|
722
796
|
break;
|
|
723
797
|
case 'read_table_schema':
|
|
724
|
-
result = await mysqlMCP.readTableSchema(args);
|
|
798
|
+
result = await mysqlMCP.readTableSchema((args || {}));
|
|
725
799
|
break;
|
|
726
800
|
case 'create_record':
|
|
727
|
-
result = await mysqlMCP.createRecord(args);
|
|
801
|
+
result = await mysqlMCP.createRecord((args || {}));
|
|
728
802
|
break;
|
|
729
803
|
case 'read_records':
|
|
730
|
-
result = await mysqlMCP.readRecords(args);
|
|
804
|
+
result = await mysqlMCP.readRecords((args || {}));
|
|
731
805
|
break;
|
|
732
806
|
case 'update_record':
|
|
733
|
-
result = await mysqlMCP.updateRecord(args);
|
|
807
|
+
result = await mysqlMCP.updateRecord((args || {}));
|
|
734
808
|
break;
|
|
735
809
|
case 'delete_record':
|
|
736
|
-
result = await mysqlMCP.deleteRecord(args);
|
|
810
|
+
result = await mysqlMCP.deleteRecord((args || {}));
|
|
737
811
|
break;
|
|
738
812
|
case 'bulk_insert':
|
|
739
|
-
result = await mysqlMCP.bulkInsert(args);
|
|
813
|
+
result = await mysqlMCP.bulkInsert((args || {}));
|
|
740
814
|
break;
|
|
741
815
|
case 'bulk_update':
|
|
742
|
-
result = await mysqlMCP.bulkUpdate(args);
|
|
816
|
+
result = await mysqlMCP.bulkUpdate((args || {}));
|
|
743
817
|
break;
|
|
744
818
|
case 'bulk_delete':
|
|
745
|
-
result = await mysqlMCP.bulkDelete(args);
|
|
819
|
+
result = await mysqlMCP.bulkDelete((args || {}));
|
|
746
820
|
break;
|
|
747
821
|
case 'run_query':
|
|
748
|
-
result = await mysqlMCP.runQuery(args);
|
|
822
|
+
result = await mysqlMCP.runQuery((args || {}));
|
|
749
823
|
break;
|
|
750
824
|
case 'execute_sql':
|
|
751
|
-
result = await mysqlMCP.executeSql(args);
|
|
825
|
+
result = await mysqlMCP.executeSql((args || {}));
|
|
752
826
|
break;
|
|
753
827
|
case 'create_table':
|
|
754
|
-
result = await mysqlMCP.createTable(args);
|
|
828
|
+
result = await mysqlMCP.createTable(args || {});
|
|
755
829
|
break;
|
|
756
830
|
case 'alter_table':
|
|
757
|
-
result = await mysqlMCP.alterTable(args);
|
|
831
|
+
result = await mysqlMCP.alterTable(args || {});
|
|
758
832
|
break;
|
|
759
833
|
case 'drop_table':
|
|
760
|
-
result = await mysqlMCP.dropTable(args);
|
|
834
|
+
result = await mysqlMCP.dropTable(args || {});
|
|
761
835
|
break;
|
|
762
836
|
case 'execute_ddl':
|
|
763
|
-
result = await mysqlMCP.executeDdl(args);
|
|
837
|
+
result = await mysqlMCP.executeDdl((args || {}));
|
|
764
838
|
break;
|
|
765
839
|
case 'describe_connection':
|
|
766
840
|
result = await mysqlMCP.describeConnection();
|
|
@@ -769,42 +843,49 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
769
843
|
result = await mysqlMCP.testConnection();
|
|
770
844
|
break;
|
|
771
845
|
case 'get_table_relationships':
|
|
772
|
-
result = await mysqlMCP.getTableRelationships(args);
|
|
846
|
+
result = await mysqlMCP.getTableRelationships((args || {}));
|
|
773
847
|
break;
|
|
774
848
|
// Transaction Tools
|
|
775
849
|
case 'begin_transaction':
|
|
776
|
-
result = await mysqlMCP.beginTransaction(args);
|
|
850
|
+
result = await mysqlMCP.beginTransaction((args || {}));
|
|
777
851
|
break;
|
|
778
852
|
case 'commit_transaction':
|
|
779
|
-
result = await mysqlMCP.commitTransaction(args);
|
|
853
|
+
result = await mysqlMCP.commitTransaction((args || {}));
|
|
780
854
|
break;
|
|
781
855
|
case 'rollback_transaction':
|
|
782
|
-
result = await mysqlMCP.rollbackTransaction(args);
|
|
856
|
+
result = await mysqlMCP.rollbackTransaction((args || {}));
|
|
783
857
|
break;
|
|
784
858
|
case 'get_transaction_status':
|
|
785
859
|
result = await mysqlMCP.getTransactionStatus();
|
|
786
860
|
break;
|
|
787
861
|
case 'execute_in_transaction':
|
|
788
|
-
result = await mysqlMCP.executeInTransaction(args);
|
|
862
|
+
result = await mysqlMCP.executeInTransaction((args || {}));
|
|
789
863
|
break;
|
|
790
864
|
// Stored Procedure Tools
|
|
791
865
|
case 'list_stored_procedures':
|
|
792
|
-
result = await mysqlMCP.listStoredProcedures(args);
|
|
866
|
+
result = await mysqlMCP.listStoredProcedures((args || {}));
|
|
793
867
|
break;
|
|
794
868
|
case 'get_stored_procedure_info':
|
|
795
|
-
result = await mysqlMCP.getStoredProcedureInfo(args);
|
|
869
|
+
result = await mysqlMCP.getStoredProcedureInfo((args || {}));
|
|
796
870
|
break;
|
|
797
871
|
case 'execute_stored_procedure':
|
|
798
|
-
result = await mysqlMCP.executeStoredProcedure(args);
|
|
872
|
+
result = await mysqlMCP.executeStoredProcedure((args || {}));
|
|
799
873
|
break;
|
|
800
874
|
case 'create_stored_procedure':
|
|
801
|
-
result = await mysqlMCP.createStoredProcedure(args);
|
|
875
|
+
result = await mysqlMCP.createStoredProcedure((args || {}));
|
|
802
876
|
break;
|
|
803
877
|
case 'drop_stored_procedure':
|
|
804
|
-
result = await mysqlMCP.dropStoredProcedure(args);
|
|
878
|
+
result = await mysqlMCP.dropStoredProcedure((args || {}));
|
|
805
879
|
break;
|
|
806
880
|
case 'show_create_procedure':
|
|
807
|
-
result = await mysqlMCP.showCreateProcedure(args);
|
|
881
|
+
result = await mysqlMCP.showCreateProcedure((args || {}));
|
|
882
|
+
break;
|
|
883
|
+
// Data Export Tools
|
|
884
|
+
case 'export_table_to_csv':
|
|
885
|
+
result = await mysqlMCP.exportTableToCSV((args || {}));
|
|
886
|
+
break;
|
|
887
|
+
case 'export_query_to_csv':
|
|
888
|
+
result = await mysqlMCP.exportQueryToCSV((args || {}));
|
|
808
889
|
break;
|
|
809
890
|
default:
|
|
810
891
|
throw new Error(`Unknown tool: ${name}`);
|
|
@@ -857,6 +938,19 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
857
938
|
};
|
|
858
939
|
}
|
|
859
940
|
catch (error) {
|
|
941
|
+
// Check if this is a permission error
|
|
942
|
+
if (error.message && error.message.includes('Permission denied')) {
|
|
943
|
+
return {
|
|
944
|
+
content: [
|
|
945
|
+
{
|
|
946
|
+
type: 'text',
|
|
947
|
+
text: `❌ ${error.message}`,
|
|
948
|
+
},
|
|
949
|
+
],
|
|
950
|
+
isError: true,
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
// Handle other errors with generic message
|
|
860
954
|
return {
|
|
861
955
|
content: [
|
|
862
956
|
{
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FilterCondition, Pagination, Sorting } from '../validation/schemas';
|
|
2
|
+
import SecurityLayer from '../security/securityLayer';
|
|
3
|
+
export declare class DataExportTools {
|
|
4
|
+
private db;
|
|
5
|
+
private security;
|
|
6
|
+
constructor(security: SecurityLayer);
|
|
7
|
+
/**
|
|
8
|
+
* Export table data to CSV format
|
|
9
|
+
*/
|
|
10
|
+
exportTableToCSV(params: {
|
|
11
|
+
table_name: string;
|
|
12
|
+
filters?: FilterCondition[];
|
|
13
|
+
pagination?: Pagination;
|
|
14
|
+
sorting?: Sorting;
|
|
15
|
+
include_headers?: boolean;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
status: string;
|
|
18
|
+
data?: any;
|
|
19
|
+
error?: string;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Export query results to CSV format
|
|
23
|
+
*/
|
|
24
|
+
exportQueryToCSV(params: {
|
|
25
|
+
query: string;
|
|
26
|
+
params?: any[];
|
|
27
|
+
include_headers?: boolean;
|
|
28
|
+
}): Promise<{
|
|
29
|
+
status: string;
|
|
30
|
+
data?: any;
|
|
31
|
+
error?: string;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DataExportTools = void 0;
|
|
7
|
+
const connection_1 = __importDefault(require("../db/connection"));
|
|
8
|
+
class DataExportTools {
|
|
9
|
+
constructor(security) {
|
|
10
|
+
this.db = connection_1.default.getInstance();
|
|
11
|
+
this.security = security;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Export table data to CSV format
|
|
15
|
+
*/
|
|
16
|
+
async exportTableToCSV(params) {
|
|
17
|
+
try {
|
|
18
|
+
const { table_name, filters = [], pagination, sorting, include_headers = true } = params;
|
|
19
|
+
// Validate table name
|
|
20
|
+
const tableValidation = this.security.validateIdentifier(table_name);
|
|
21
|
+
if (!tableValidation.valid) {
|
|
22
|
+
return {
|
|
23
|
+
status: 'error',
|
|
24
|
+
error: tableValidation.error
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// Build WHERE clause
|
|
28
|
+
let whereClause = '';
|
|
29
|
+
const whereParams = [];
|
|
30
|
+
if (filters && filters.length > 0) {
|
|
31
|
+
const whereConditions = [];
|
|
32
|
+
for (const filter of filters) {
|
|
33
|
+
// Validate field name
|
|
34
|
+
const fieldValidation = this.security.validateIdentifier(filter.field);
|
|
35
|
+
if (!fieldValidation.valid) {
|
|
36
|
+
return {
|
|
37
|
+
status: 'error',
|
|
38
|
+
error: `Invalid field name: ${filter.field}`
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const fieldName = this.security.escapeIdentifier(filter.field);
|
|
42
|
+
switch (filter.operator) {
|
|
43
|
+
case 'eq':
|
|
44
|
+
whereConditions.push(`${fieldName} = ?`);
|
|
45
|
+
whereParams.push(filter.value);
|
|
46
|
+
break;
|
|
47
|
+
case 'neq':
|
|
48
|
+
whereConditions.push(`${fieldName} != ?`);
|
|
49
|
+
whereParams.push(filter.value);
|
|
50
|
+
break;
|
|
51
|
+
case 'gt':
|
|
52
|
+
whereConditions.push(`${fieldName} > ?`);
|
|
53
|
+
whereParams.push(filter.value);
|
|
54
|
+
break;
|
|
55
|
+
case 'gte':
|
|
56
|
+
whereConditions.push(`${fieldName} >= ?`);
|
|
57
|
+
whereParams.push(filter.value);
|
|
58
|
+
break;
|
|
59
|
+
case 'lt':
|
|
60
|
+
whereConditions.push(`${fieldName} < ?`);
|
|
61
|
+
whereParams.push(filter.value);
|
|
62
|
+
break;
|
|
63
|
+
case 'lte':
|
|
64
|
+
whereConditions.push(`${fieldName} <= ?`);
|
|
65
|
+
whereParams.push(filter.value);
|
|
66
|
+
break;
|
|
67
|
+
case 'like':
|
|
68
|
+
whereConditions.push(`${fieldName} LIKE ?`);
|
|
69
|
+
whereParams.push(filter.value);
|
|
70
|
+
break;
|
|
71
|
+
case 'in':
|
|
72
|
+
if (Array.isArray(filter.value)) {
|
|
73
|
+
const placeholders = filter.value.map(() => '?').join(', ');
|
|
74
|
+
whereConditions.push(`${fieldName} IN (${placeholders})`);
|
|
75
|
+
whereParams.push(...filter.value);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
return {
|
|
79
|
+
status: 'error',
|
|
80
|
+
error: 'IN operator requires an array of values'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
return {
|
|
86
|
+
status: 'error',
|
|
87
|
+
error: `Unsupported operator: ${filter.operator}`
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (whereConditions.length > 0) {
|
|
92
|
+
whereClause = 'WHERE ' + whereConditions.join(' AND ');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Build ORDER BY clause
|
|
96
|
+
let orderByClause = '';
|
|
97
|
+
if (sorting) {
|
|
98
|
+
const fieldValidation = this.security.validateIdentifier(sorting.field);
|
|
99
|
+
if (!fieldValidation.valid) {
|
|
100
|
+
return {
|
|
101
|
+
status: 'error',
|
|
102
|
+
error: `Invalid sort field name: ${sorting.field}`
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const fieldName = this.security.escapeIdentifier(sorting.field);
|
|
106
|
+
const direction = sorting.direction.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
|
107
|
+
orderByClause = `ORDER BY ${fieldName} ${direction}`;
|
|
108
|
+
}
|
|
109
|
+
// Build LIMIT clause
|
|
110
|
+
let limitClause = '';
|
|
111
|
+
if (pagination) {
|
|
112
|
+
const offset = (pagination.page - 1) * pagination.limit;
|
|
113
|
+
limitClause = `LIMIT ${offset}, ${pagination.limit}`;
|
|
114
|
+
}
|
|
115
|
+
// Construct the query
|
|
116
|
+
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
117
|
+
const query = `SELECT * FROM ${escapedTableName} ${whereClause} ${orderByClause} ${limitClause}`;
|
|
118
|
+
// Execute query
|
|
119
|
+
const results = await this.db.query(query, whereParams);
|
|
120
|
+
// If no results, return empty CSV
|
|
121
|
+
if (results.length === 0) {
|
|
122
|
+
return {
|
|
123
|
+
status: 'success',
|
|
124
|
+
data: {
|
|
125
|
+
csv: include_headers ? '' : '',
|
|
126
|
+
row_count: 0
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// Generate CSV
|
|
131
|
+
let csv = '';
|
|
132
|
+
// Add headers if requested
|
|
133
|
+
if (include_headers) {
|
|
134
|
+
const headers = Object.keys(results[0]).join(',');
|
|
135
|
+
csv += headers + '\n';
|
|
136
|
+
}
|
|
137
|
+
// Add data rows
|
|
138
|
+
for (const row of results) {
|
|
139
|
+
const values = Object.values(row).map(value => {
|
|
140
|
+
if (value === null)
|
|
141
|
+
return '';
|
|
142
|
+
if (typeof value === 'string') {
|
|
143
|
+
// Escape quotes and wrap in quotes if contains comma or newline
|
|
144
|
+
if (value.includes(',') || value.includes('\n') || value.includes('"')) {
|
|
145
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
return String(value);
|
|
150
|
+
}).join(',');
|
|
151
|
+
csv += values + '\n';
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
status: 'success',
|
|
155
|
+
data: {
|
|
156
|
+
csv: csv,
|
|
157
|
+
row_count: results.length
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
return {
|
|
163
|
+
status: 'error',
|
|
164
|
+
error: error.message
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Export query results to CSV format
|
|
170
|
+
*/
|
|
171
|
+
async exportQueryToCSV(params) {
|
|
172
|
+
try {
|
|
173
|
+
const { query, params: queryParams = [], include_headers = true } = params;
|
|
174
|
+
// Validate query is a SELECT statement
|
|
175
|
+
if (!this.security.isReadOnlyQuery(query)) {
|
|
176
|
+
return {
|
|
177
|
+
status: 'error',
|
|
178
|
+
error: 'Only SELECT queries can be exported to CSV'
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// Validate parameters
|
|
182
|
+
const paramValidation = this.security.validateParameters(queryParams);
|
|
183
|
+
if (!paramValidation.valid) {
|
|
184
|
+
return {
|
|
185
|
+
status: 'error',
|
|
186
|
+
error: paramValidation.error
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// Execute query
|
|
190
|
+
const results = await this.db.query(query, queryParams);
|
|
191
|
+
// If no results, return empty CSV
|
|
192
|
+
if (results.length === 0) {
|
|
193
|
+
return {
|
|
194
|
+
status: 'success',
|
|
195
|
+
data: {
|
|
196
|
+
csv: include_headers ? '' : '',
|
|
197
|
+
row_count: 0
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Generate CSV
|
|
202
|
+
let csv = '';
|
|
203
|
+
// Add headers if requested
|
|
204
|
+
if (include_headers) {
|
|
205
|
+
const headers = Object.keys(results[0]).join(',');
|
|
206
|
+
csv += headers + '\n';
|
|
207
|
+
}
|
|
208
|
+
// Add data rows
|
|
209
|
+
for (const row of results) {
|
|
210
|
+
const values = Object.values(row).map(value => {
|
|
211
|
+
if (value === null)
|
|
212
|
+
return '';
|
|
213
|
+
if (typeof value === 'string') {
|
|
214
|
+
// Escape quotes and wrap in quotes if contains comma or newline
|
|
215
|
+
if (value.includes(',') || value.includes('\n') || value.includes('"')) {
|
|
216
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
217
|
+
}
|
|
218
|
+
return value;
|
|
219
|
+
}
|
|
220
|
+
return String(value);
|
|
221
|
+
}).join(',');
|
|
222
|
+
csv += values + '\n';
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
status: 'success',
|
|
226
|
+
data: {
|
|
227
|
+
csv: csv,
|
|
228
|
+
row_count: results.length
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
return {
|
|
234
|
+
status: 'error',
|
|
235
|
+
error: error.message
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
exports.DataExportTools = DataExportTools;
|
|
@@ -512,6 +512,87 @@ export declare const showCreateProcedureSchema: {
|
|
|
512
512
|
};
|
|
513
513
|
additionalProperties: boolean;
|
|
514
514
|
};
|
|
515
|
+
export declare const exportTableToCsvSchema: {
|
|
516
|
+
type: string;
|
|
517
|
+
required: string[];
|
|
518
|
+
properties: {
|
|
519
|
+
table_name: {
|
|
520
|
+
type: string;
|
|
521
|
+
};
|
|
522
|
+
filters: {
|
|
523
|
+
type: string;
|
|
524
|
+
items: {
|
|
525
|
+
type: string;
|
|
526
|
+
required: string[];
|
|
527
|
+
properties: {
|
|
528
|
+
field: {
|
|
529
|
+
type: string;
|
|
530
|
+
};
|
|
531
|
+
operator: {
|
|
532
|
+
type: string;
|
|
533
|
+
enum: string[];
|
|
534
|
+
};
|
|
535
|
+
value: {};
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
nullable: boolean;
|
|
539
|
+
};
|
|
540
|
+
pagination: {
|
|
541
|
+
type: string;
|
|
542
|
+
properties: {
|
|
543
|
+
page: {
|
|
544
|
+
type: string;
|
|
545
|
+
minimum: number;
|
|
546
|
+
};
|
|
547
|
+
limit: {
|
|
548
|
+
type: string;
|
|
549
|
+
minimum: number;
|
|
550
|
+
maximum: number;
|
|
551
|
+
};
|
|
552
|
+
};
|
|
553
|
+
required: string[];
|
|
554
|
+
nullable: boolean;
|
|
555
|
+
};
|
|
556
|
+
sorting: {
|
|
557
|
+
type: string;
|
|
558
|
+
properties: {
|
|
559
|
+
field: {
|
|
560
|
+
type: string;
|
|
561
|
+
};
|
|
562
|
+
direction: {
|
|
563
|
+
type: string;
|
|
564
|
+
enum: string[];
|
|
565
|
+
};
|
|
566
|
+
};
|
|
567
|
+
required: string[];
|
|
568
|
+
nullable: boolean;
|
|
569
|
+
};
|
|
570
|
+
include_headers: {
|
|
571
|
+
type: string;
|
|
572
|
+
nullable: boolean;
|
|
573
|
+
};
|
|
574
|
+
};
|
|
575
|
+
additionalProperties: boolean;
|
|
576
|
+
};
|
|
577
|
+
export declare const exportQueryToCsvSchema: {
|
|
578
|
+
type: string;
|
|
579
|
+
required: string[];
|
|
580
|
+
properties: {
|
|
581
|
+
query: {
|
|
582
|
+
type: string;
|
|
583
|
+
};
|
|
584
|
+
params: {
|
|
585
|
+
type: string;
|
|
586
|
+
items: {};
|
|
587
|
+
nullable: boolean;
|
|
588
|
+
};
|
|
589
|
+
include_headers: {
|
|
590
|
+
type: string;
|
|
591
|
+
nullable: boolean;
|
|
592
|
+
};
|
|
593
|
+
};
|
|
594
|
+
additionalProperties: boolean;
|
|
595
|
+
};
|
|
515
596
|
export declare const validateListTables: import("ajv").ValidateFunction<{
|
|
516
597
|
database: unknown;
|
|
517
598
|
} & {}>;
|
|
@@ -596,3 +677,9 @@ export declare const validateStoredProcedureCreation: import("ajv").ValidateFunc
|
|
|
596
677
|
export declare const validateGetTableRelationships: import("ajv").ValidateFunction<{
|
|
597
678
|
[x: string]: {};
|
|
598
679
|
}>;
|
|
680
|
+
export declare const validateExportTableToCsv: import("ajv").ValidateFunction<{
|
|
681
|
+
[x: string]: {};
|
|
682
|
+
}>;
|
|
683
|
+
export declare const validateExportQueryToCsv: import("ajv").ValidateFunction<{
|
|
684
|
+
[x: string]: {};
|
|
685
|
+
}>;
|