@bytebase/dbhub 0.2.2 → 0.2.3
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 +21 -18
- package/dist/index.js +58 -55
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,14 +36,14 @@ https://demo.dbhub.ai/sse connects a [sample employee database](https://github.c
|
|
|
36
36
|
|
|
37
37
|
### Database Resources
|
|
38
38
|
|
|
39
|
-
| Resource Name
|
|
40
|
-
|
|
|
41
|
-
| schemas
|
|
42
|
-
| tables_in_schema
|
|
43
|
-
| table_structure_in_schema
|
|
44
|
-
| indexes_in_table
|
|
45
|
-
| procedures_in_schema
|
|
46
|
-
| procedure_details_in_schema
|
|
39
|
+
| Resource Name | URI Format | PostgreSQL | MySQL | SQL Server | SQLite |
|
|
40
|
+
| --------------------------- | ------------------------------------------------------ | :--------: | :---: | :--------: | :----: |
|
|
41
|
+
| schemas | `db://schemas` | ✅ | ✅ | ✅ | ✅ |
|
|
42
|
+
| tables_in_schema | `db://schemas/{schemaName}/tables` | ✅ | ✅ | ✅ | ✅ |
|
|
43
|
+
| table_structure_in_schema | `db://schemas/{schemaName}/tables/{tableName}` | ✅ | ✅ | ✅ | ✅ |
|
|
44
|
+
| indexes_in_table | `db://schemas/{schemaName}/tables/{tableName}/indexes` | ✅ | ✅ | ✅ | ✅ |
|
|
45
|
+
| procedures_in_schema | `db://schemas/{schemaName}/procedures` | ✅ | ✅ | ✅ | ❌ |
|
|
46
|
+
| procedure_details_in_schema | `db://schemas/{schemaName}/procedures/{procedureName}` | ✅ | ✅ | ✅ | ❌ |
|
|
47
47
|
|
|
48
48
|
### Database Tools
|
|
49
49
|
|
|
@@ -54,10 +54,10 @@ https://demo.dbhub.ai/sse connects a [sample employee database](https://github.c
|
|
|
54
54
|
|
|
55
55
|
### Prompt Capabilities
|
|
56
56
|
|
|
57
|
-
| Prompt | Command Name
|
|
58
|
-
| ------------------- |
|
|
59
|
-
| Generate SQL | `generate_sql`
|
|
60
|
-
| Explain DB Elements | `explain_db`
|
|
57
|
+
| Prompt | Command Name | PostgreSQL | MySQL | SQL Server | SQLite |
|
|
58
|
+
| ------------------- | -------------- | :--------: | :---: | :--------: | :----: |
|
|
59
|
+
| Generate SQL | `generate_sql` | ✅ | ✅ | ✅ | ✅ |
|
|
60
|
+
| Explain DB Elements | `explain_db` | ✅ | ✅ | ✅ | ✅ |
|
|
61
61
|
|
|
62
62
|
## Installation
|
|
63
63
|
|
|
@@ -181,14 +181,17 @@ For real databases, a Database Source Name (DSN) is required. You can provide th
|
|
|
181
181
|
DSN=postgres://user:password@localhost:5432/dbname?sslmode=disable
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
> [!WARNING]
|
|
185
|
+
> When running in Docker, use `host.docker.internal` instead of `localhost` to connect to databases running on your host machine. For example: `mysql://user:password@host.docker.internal:3306/dbname`
|
|
186
|
+
|
|
184
187
|
DBHub supports the following database connection string formats:
|
|
185
188
|
|
|
186
|
-
| Database | DSN Format | Example
|
|
187
|
-
| ---------- | -------------------------------------------------------- |
|
|
188
|
-
| PostgreSQL | `postgres://[user]:[password]@[host]:[port]/[database]` | `postgres://user:password@localhost:5432/dbname?sslmode=disable`
|
|
189
|
-
| SQLite | `sqlite:///[path/to/file]` or `sqlite::memory:` | `sqlite:///path/to/database.db` or `sqlite::memory:`
|
|
190
|
-
| SQL Server | `sqlserver://[user]:[password]@[host]:[port]/[database]` | `sqlserver://user:password@localhost:1433/dbname`
|
|
191
|
-
| MySQL | `mysql://[user]:[password]@[host]:[port]/[database]` | `mysql://user:password@localhost:3306/dbname`
|
|
189
|
+
| Database | DSN Format | Example |
|
|
190
|
+
| ---------- | -------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
191
|
+
| PostgreSQL | `postgres://[user]:[password]@[host]:[port]/[database]` | `postgres://user:password@localhost:5432/dbname?sslmode=disable` |
|
|
192
|
+
| SQLite | `sqlite:///[path/to/file]` or `sqlite::memory:` | `sqlite:///path/to/database.db` or `sqlite::memory:` |
|
|
193
|
+
| SQL Server | `sqlserver://[user]:[password]@[host]:[port]/[database]` | `sqlserver://user:password@localhost:1433/dbname` |
|
|
194
|
+
| MySQL | `mysql://[user]:[password]@[host]:[port]/[database]` | `mysql://user:password@localhost:3306/dbname` |
|
|
192
195
|
|
|
193
196
|
### Transport
|
|
194
197
|
|
package/dist/index.js
CHANGED
|
@@ -974,11 +974,11 @@ var MySQLConnector = class {
|
|
|
974
974
|
}
|
|
975
975
|
try {
|
|
976
976
|
const [rows] = await this.pool.query(`
|
|
977
|
-
SELECT
|
|
978
|
-
FROM
|
|
979
|
-
ORDER BY
|
|
977
|
+
SELECT SCHEMA_NAME
|
|
978
|
+
FROM INFORMATION_SCHEMA.SCHEMATA
|
|
979
|
+
ORDER BY SCHEMA_NAME
|
|
980
980
|
`);
|
|
981
|
-
return rows.map((row) => row.
|
|
981
|
+
return rows.map((row) => row.SCHEMA_NAME);
|
|
982
982
|
} catch (error) {
|
|
983
983
|
console.error("Error getting schemas:", error);
|
|
984
984
|
throw error;
|
|
@@ -989,15 +989,15 @@ var MySQLConnector = class {
|
|
|
989
989
|
throw new Error("Not connected to database");
|
|
990
990
|
}
|
|
991
991
|
try {
|
|
992
|
-
const schemaClause = schema ? "WHERE
|
|
992
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
993
993
|
const queryParams = schema ? [schema] : [];
|
|
994
994
|
const [rows] = await this.pool.query(`
|
|
995
|
-
SELECT
|
|
996
|
-
FROM
|
|
995
|
+
SELECT TABLE_NAME
|
|
996
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
997
997
|
${schemaClause}
|
|
998
|
-
ORDER BY
|
|
998
|
+
ORDER BY TABLE_NAME
|
|
999
999
|
`, queryParams);
|
|
1000
|
-
return rows.map((row) => row.
|
|
1000
|
+
return rows.map((row) => row.TABLE_NAME);
|
|
1001
1001
|
} catch (error) {
|
|
1002
1002
|
console.error("Error getting tables:", error);
|
|
1003
1003
|
throw error;
|
|
@@ -1008,15 +1008,15 @@ var MySQLConnector = class {
|
|
|
1008
1008
|
throw new Error("Not connected to database");
|
|
1009
1009
|
}
|
|
1010
1010
|
try {
|
|
1011
|
-
const schemaClause = schema ? "WHERE
|
|
1011
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
1012
1012
|
const queryParams = schema ? [schema, tableName] : [tableName];
|
|
1013
1013
|
const [rows] = await this.pool.query(`
|
|
1014
|
-
SELECT COUNT(*)
|
|
1015
|
-
FROM
|
|
1014
|
+
SELECT COUNT(*) AS COUNT
|
|
1015
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
1016
1016
|
${schemaClause}
|
|
1017
|
-
AND
|
|
1017
|
+
AND TABLE_NAME = ?
|
|
1018
1018
|
`, queryParams);
|
|
1019
|
-
return rows[0].
|
|
1019
|
+
return rows[0].COUNT > 0;
|
|
1020
1020
|
} catch (error) {
|
|
1021
1021
|
console.error("Error checking if table exists:", error);
|
|
1022
1022
|
throw error;
|
|
@@ -1036,7 +1036,7 @@ var MySQLConnector = class {
|
|
|
1036
1036
|
NON_UNIQUE,
|
|
1037
1037
|
SEQ_IN_INDEX
|
|
1038
1038
|
FROM
|
|
1039
|
-
|
|
1039
|
+
INFORMATION_SCHEMA.STATISTICS
|
|
1040
1040
|
WHERE
|
|
1041
1041
|
${schemaClause}
|
|
1042
1042
|
AND TABLE_NAME = ?
|
|
@@ -1080,18 +1080,18 @@ var MySQLConnector = class {
|
|
|
1080
1080
|
throw new Error("Not connected to database");
|
|
1081
1081
|
}
|
|
1082
1082
|
try {
|
|
1083
|
-
const schemaClause = schema ? "WHERE
|
|
1083
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
1084
1084
|
const queryParams = schema ? [schema, tableName] : [tableName];
|
|
1085
1085
|
const [rows] = await this.pool.query(`
|
|
1086
1086
|
SELECT
|
|
1087
|
-
column_name,
|
|
1088
|
-
data_type,
|
|
1089
|
-
is_nullable,
|
|
1090
|
-
column_default
|
|
1091
|
-
FROM
|
|
1087
|
+
COLUMN_NAME as column_name,
|
|
1088
|
+
DATA_TYPE as data_type,
|
|
1089
|
+
IS_NULLABLE as is_nullable,
|
|
1090
|
+
COLUMN_DEFAULT as column_default
|
|
1091
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
1092
1092
|
${schemaClause}
|
|
1093
|
-
AND
|
|
1094
|
-
ORDER BY
|
|
1093
|
+
AND TABLE_NAME = ?
|
|
1094
|
+
ORDER BY ORDINAL_POSITION
|
|
1095
1095
|
`, queryParams);
|
|
1096
1096
|
return rows;
|
|
1097
1097
|
} catch (error) {
|
|
@@ -1104,15 +1104,15 @@ var MySQLConnector = class {
|
|
|
1104
1104
|
throw new Error("Not connected to database");
|
|
1105
1105
|
}
|
|
1106
1106
|
try {
|
|
1107
|
-
const schemaClause = schema ? "WHERE
|
|
1107
|
+
const schemaClause = schema ? "WHERE ROUTINE_SCHEMA = ?" : "WHERE ROUTINE_SCHEMA = DATABASE()";
|
|
1108
1108
|
const queryParams = schema ? [schema] : [];
|
|
1109
1109
|
const [rows] = await this.pool.query(`
|
|
1110
|
-
SELECT
|
|
1111
|
-
FROM
|
|
1110
|
+
SELECT ROUTINE_NAME
|
|
1111
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
1112
1112
|
${schemaClause}
|
|
1113
|
-
ORDER BY
|
|
1113
|
+
ORDER BY ROUTINE_NAME
|
|
1114
1114
|
`, queryParams);
|
|
1115
|
-
return rows.map((row) => row.
|
|
1115
|
+
return rows.map((row) => row.ROUTINE_NAME);
|
|
1116
1116
|
} catch (error) {
|
|
1117
1117
|
console.error("Error getting stored procedures:", error);
|
|
1118
1118
|
throw error;
|
|
@@ -1123,39 +1123,39 @@ var MySQLConnector = class {
|
|
|
1123
1123
|
throw new Error("Not connected to database");
|
|
1124
1124
|
}
|
|
1125
1125
|
try {
|
|
1126
|
-
const schemaClause = schema ? "WHERE r.
|
|
1126
|
+
const schemaClause = schema ? "WHERE r.ROUTINE_SCHEMA = ?" : "WHERE r.ROUTINE_SCHEMA = DATABASE()";
|
|
1127
1127
|
const queryParams = schema ? [schema, procedureName] : [procedureName];
|
|
1128
1128
|
const [rows] = await this.pool.query(`
|
|
1129
1129
|
SELECT
|
|
1130
|
-
r.
|
|
1130
|
+
r.ROUTINE_NAME AS procedure_name,
|
|
1131
1131
|
CASE
|
|
1132
|
-
WHEN r.
|
|
1132
|
+
WHEN r.ROUTINE_TYPE = 'PROCEDURE' THEN 'procedure'
|
|
1133
1133
|
ELSE 'function'
|
|
1134
1134
|
END AS procedure_type,
|
|
1135
|
-
LOWER(r.
|
|
1136
|
-
r.
|
|
1137
|
-
r.
|
|
1135
|
+
LOWER(r.ROUTINE_TYPE) AS routine_type,
|
|
1136
|
+
r.ROUTINE_DEFINITION,
|
|
1137
|
+
r.DTD_IDENTIFIER AS return_type,
|
|
1138
1138
|
(
|
|
1139
1139
|
SELECT GROUP_CONCAT(
|
|
1140
|
-
CONCAT(p.
|
|
1141
|
-
ORDER BY p.
|
|
1140
|
+
CONCAT(p.PARAMETER_NAME, ' ', p.PARAMETER_MODE, ' ', p.DATA_TYPE)
|
|
1141
|
+
ORDER BY p.ORDINAL_POSITION
|
|
1142
1142
|
SEPARATOR ', '
|
|
1143
1143
|
)
|
|
1144
|
-
FROM
|
|
1145
|
-
WHERE p.
|
|
1146
|
-
AND p.
|
|
1147
|
-
AND p.
|
|
1144
|
+
FROM INFORMATION_SCHEMA.PARAMETERS p
|
|
1145
|
+
WHERE p.SPECIFIC_SCHEMA = r.ROUTINE_SCHEMA
|
|
1146
|
+
AND p.SPECIFIC_NAME = r.ROUTINE_NAME
|
|
1147
|
+
AND p.PARAMETER_NAME IS NOT NULL
|
|
1148
1148
|
) AS parameter_list
|
|
1149
|
-
FROM
|
|
1149
|
+
FROM INFORMATION_SCHEMA.ROUTINES r
|
|
1150
1150
|
${schemaClause}
|
|
1151
|
-
AND r.
|
|
1151
|
+
AND r.ROUTINE_NAME = ?
|
|
1152
1152
|
`, queryParams);
|
|
1153
1153
|
if (rows.length === 0) {
|
|
1154
1154
|
const schemaName = schema || "current schema";
|
|
1155
1155
|
throw new Error(`Stored procedure '${procedureName}' not found in ${schemaName}`);
|
|
1156
1156
|
}
|
|
1157
1157
|
const procedure = rows[0];
|
|
1158
|
-
let definition = procedure.
|
|
1158
|
+
let definition = procedure.ROUTINE_DEFINITION;
|
|
1159
1159
|
try {
|
|
1160
1160
|
const schemaValue = schema || await this.getCurrentSchema();
|
|
1161
1161
|
if (procedure.procedure_type === "procedure") {
|
|
@@ -1183,15 +1183,15 @@ var MySQLConnector = class {
|
|
|
1183
1183
|
}
|
|
1184
1184
|
if (!definition) {
|
|
1185
1185
|
const [bodyRows] = await this.pool.query(`
|
|
1186
|
-
SELECT
|
|
1187
|
-
FROM
|
|
1188
|
-
WHERE
|
|
1186
|
+
SELECT ROUTINE_DEFINITION, ROUTINE_BODY
|
|
1187
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
1188
|
+
WHERE ROUTINE_SCHEMA = ? AND ROUTINE_NAME = ?
|
|
1189
1189
|
`, [schemaValue, procedureName]);
|
|
1190
1190
|
if (bodyRows && bodyRows.length > 0) {
|
|
1191
|
-
if (bodyRows[0].
|
|
1192
|
-
definition = bodyRows[0].
|
|
1193
|
-
} else if (bodyRows[0].
|
|
1194
|
-
definition = bodyRows[0].
|
|
1191
|
+
if (bodyRows[0].ROUTINE_DEFINITION) {
|
|
1192
|
+
definition = bodyRows[0].ROUTINE_DEFINITION;
|
|
1193
|
+
} else if (bodyRows[0].ROUTINE_BODY) {
|
|
1194
|
+
definition = bodyRows[0].ROUTINE_BODY;
|
|
1195
1195
|
}
|
|
1196
1196
|
}
|
|
1197
1197
|
}
|
|
@@ -1214,8 +1214,8 @@ var MySQLConnector = class {
|
|
|
1214
1214
|
}
|
|
1215
1215
|
// Helper method to get current schema (database) name
|
|
1216
1216
|
async getCurrentSchema() {
|
|
1217
|
-
const [rows] = await this.pool.query("SELECT DATABASE()
|
|
1218
|
-
return rows[0].
|
|
1217
|
+
const [rows] = await this.pool.query("SELECT DATABASE() AS DB");
|
|
1218
|
+
return rows[0].DB;
|
|
1219
1219
|
}
|
|
1220
1220
|
async executeQuery(query) {
|
|
1221
1221
|
if (!this.pool) {
|
|
@@ -1595,7 +1595,7 @@ async function tablesResourceHandler(uri, variables, _extra) {
|
|
|
1595
1595
|
}
|
|
1596
1596
|
|
|
1597
1597
|
// src/resources/schema.ts
|
|
1598
|
-
async function
|
|
1598
|
+
async function tableStructureResourceHandler(uri, variables, _extra) {
|
|
1599
1599
|
const connector = ConnectorManager.getCurrentConnector();
|
|
1600
1600
|
const tableName = Array.isArray(variables.tableName) ? variables.tableName[0] : variables.tableName;
|
|
1601
1601
|
const schemaName = variables.schemaName ? Array.isArray(variables.schemaName) ? variables.schemaName[0] : variables.schemaName : void 0;
|
|
@@ -1796,7 +1796,7 @@ function registerResources(server) {
|
|
|
1796
1796
|
server.resource(
|
|
1797
1797
|
"table_structure_in_schema",
|
|
1798
1798
|
new ResourceTemplate("db://schemas/{schemaName}/tables/{tableName}", { list: void 0 }),
|
|
1799
|
-
|
|
1799
|
+
tableStructureResourceHandler
|
|
1800
1800
|
);
|
|
1801
1801
|
server.resource(
|
|
1802
1802
|
"indexes_in_table",
|
|
@@ -1846,7 +1846,6 @@ async function runQueryToolHandler({ query }, _extra) {
|
|
|
1846
1846
|
|
|
1847
1847
|
// src/tools/list-connectors.ts
|
|
1848
1848
|
async function listConnectorsToolHandler(_args, _extra) {
|
|
1849
|
-
const connectors = ConnectorManager.getAvailableConnectors();
|
|
1850
1849
|
const samples = ConnectorRegistry.getAllSampleDSNs();
|
|
1851
1850
|
let activeConnectorId = null;
|
|
1852
1851
|
try {
|
|
@@ -2169,6 +2168,10 @@ ${possibleTableMatches.join("\n")}`
|
|
|
2169
2168
|
"EXPLANATION_ERROR"
|
|
2170
2169
|
);
|
|
2171
2170
|
}
|
|
2171
|
+
return formatPromptErrorResponse(
|
|
2172
|
+
`Unable to process request for schema: ${schema}, table: ${table}`,
|
|
2173
|
+
"UNHANDLED_REQUEST"
|
|
2174
|
+
);
|
|
2172
2175
|
}
|
|
2173
2176
|
function determineTablePurpose(tableName, columns) {
|
|
2174
2177
|
const lowerTableName = tableName.toLowerCase();
|