@bytebase/dbhub 0.1.0 → 0.1.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.
Files changed (33) hide show
  1. package/README.md +34 -33
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +1489 -13
  4. package/dist/resources/employee-sqlite/employee.sql +117 -0
  5. package/dist/resources/employee-sqlite/load_department.sql +10 -0
  6. package/dist/resources/employee-sqlite/load_dept_emp.sql +1103 -0
  7. package/dist/resources/employee-sqlite/load_dept_manager.sql +17 -0
  8. package/dist/resources/employee-sqlite/load_employee.sql +1000 -0
  9. package/dist/resources/employee-sqlite/load_salary1.sql +9488 -0
  10. package/dist/resources/employee-sqlite/load_title.sql +1470 -0
  11. package/dist/resources/employee-sqlite/object.sql +74 -0
  12. package/dist/resources/employee-sqlite/show_elapsed.sql +4 -0
  13. package/dist/resources/employee-sqlite/test_employee_md5.sql +119 -0
  14. package/package.json +5 -4
  15. package/dist/config/demo-loader.js +0 -45
  16. package/dist/config/env.js +0 -146
  17. package/dist/connectors/interface.js +0 -55
  18. package/dist/connectors/manager.js +0 -91
  19. package/dist/connectors/mysql/index.js +0 -169
  20. package/dist/connectors/postgres/index.js +0 -172
  21. package/dist/connectors/sqlite/index.js +0 -208
  22. package/dist/connectors/sqlserver/index.js +0 -186
  23. package/dist/prompts/db-explainer.js +0 -201
  24. package/dist/prompts/index.js +0 -11
  25. package/dist/prompts/sql-generator.js +0 -114
  26. package/dist/resources/index.js +0 -12
  27. package/dist/resources/schema.js +0 -36
  28. package/dist/resources/tables.js +0 -17
  29. package/dist/server.js +0 -140
  30. package/dist/tools/index.js +0 -11
  31. package/dist/tools/list-connectors.js +0 -43
  32. package/dist/tools/run-query.js +0 -32
  33. package/dist/utils/response-formatter.js +0 -109
@@ -1,169 +0,0 @@
1
- import mysql from 'mysql2/promise';
2
- import { ConnectorRegistry } from '../interface.js';
3
- /**
4
- * MySQL DSN Parser
5
- * Handles DSN strings like: mysql://user:password@localhost:3306/dbname
6
- */
7
- class MySQLDSNParser {
8
- parse(dsn) {
9
- // Basic validation
10
- if (!this.isValidDSN(dsn)) {
11
- throw new Error(`Invalid MySQL DSN: ${dsn}`);
12
- }
13
- try {
14
- const url = new URL(dsn);
15
- const config = {
16
- host: url.hostname,
17
- port: url.port ? parseInt(url.port) : 3306,
18
- database: url.pathname.substring(1), // Remove leading '/'
19
- user: url.username,
20
- password: url.password,
21
- };
22
- // Handle query parameters
23
- url.searchParams.forEach((value, key) => {
24
- if (key === 'ssl') {
25
- config.ssl = value === 'true' ? {} : undefined;
26
- }
27
- // Add other parameters as needed
28
- });
29
- return config;
30
- }
31
- catch (error) {
32
- throw new Error(`Failed to parse MySQL DSN: ${error instanceof Error ? error.message : String(error)}`);
33
- }
34
- }
35
- getSampleDSN() {
36
- return 'mysql://root:password@localhost:3306/mysql';
37
- }
38
- isValidDSN(dsn) {
39
- try {
40
- const url = new URL(dsn);
41
- return url.protocol === 'mysql:';
42
- }
43
- catch (error) {
44
- return false;
45
- }
46
- }
47
- }
48
- /**
49
- * MySQL Connector Implementation
50
- */
51
- export class MySQLConnector {
52
- constructor() {
53
- this.id = 'mysql';
54
- this.name = 'MySQL';
55
- this.dsnParser = new MySQLDSNParser();
56
- this.pool = null;
57
- }
58
- async connect(dsn) {
59
- try {
60
- const config = this.dsnParser.parse(dsn);
61
- this.pool = mysql.createPool(config);
62
- // Test the connection
63
- const [rows] = await this.pool.query('SELECT 1');
64
- console.error("Successfully connected to MySQL database");
65
- }
66
- catch (err) {
67
- console.error("Failed to connect to MySQL database:", err);
68
- throw err;
69
- }
70
- }
71
- async disconnect() {
72
- if (this.pool) {
73
- await this.pool.end();
74
- this.pool = null;
75
- }
76
- }
77
- async getTables() {
78
- if (!this.pool) {
79
- throw new Error('Not connected to database');
80
- }
81
- try {
82
- // Get all tables from the current database
83
- const [rows] = await this.pool.query(`
84
- SELECT table_name
85
- FROM information_schema.tables
86
- WHERE table_schema = DATABASE()
87
- ORDER BY table_name
88
- `);
89
- return rows.map(row => row.table_name);
90
- }
91
- catch (error) {
92
- console.error("Error getting tables:", error);
93
- throw error;
94
- }
95
- }
96
- async tableExists(tableName) {
97
- if (!this.pool) {
98
- throw new Error('Not connected to database');
99
- }
100
- try {
101
- const [rows] = await this.pool.query(`
102
- SELECT COUNT(*) as count
103
- FROM information_schema.tables
104
- WHERE table_schema = DATABASE()
105
- AND table_name = ?
106
- `, [tableName]);
107
- return rows[0].count > 0;
108
- }
109
- catch (error) {
110
- console.error("Error checking if table exists:", error);
111
- throw error;
112
- }
113
- }
114
- async getTableSchema(tableName) {
115
- if (!this.pool) {
116
- throw new Error('Not connected to database');
117
- }
118
- try {
119
- // Get table columns
120
- const [rows] = await this.pool.query(`
121
- SELECT
122
- column_name,
123
- data_type,
124
- is_nullable,
125
- column_default
126
- FROM information_schema.columns
127
- WHERE table_schema = DATABASE()
128
- AND table_name = ?
129
- ORDER BY ordinal_position
130
- `, [tableName]);
131
- return rows;
132
- }
133
- catch (error) {
134
- console.error("Error getting table schema:", error);
135
- throw error;
136
- }
137
- }
138
- async executeQuery(query) {
139
- if (!this.pool) {
140
- throw new Error('Not connected to database');
141
- }
142
- const safetyCheck = this.validateQuery(query);
143
- if (!safetyCheck.isValid) {
144
- throw new Error(safetyCheck.message || "Query validation failed");
145
- }
146
- try {
147
- const [rows, fields] = await this.pool.query(query);
148
- return { rows, fields };
149
- }
150
- catch (error) {
151
- console.error("Error executing query:", error);
152
- throw error;
153
- }
154
- }
155
- validateQuery(query) {
156
- // Basic check to prevent non-SELECT queries
157
- const normalizedQuery = query.trim().toLowerCase();
158
- if (!normalizedQuery.startsWith('select')) {
159
- return {
160
- isValid: false,
161
- message: "Only SELECT queries are allowed for security reasons."
162
- };
163
- }
164
- return { isValid: true };
165
- }
166
- }
167
- // Create and register the connector
168
- const mysqlConnector = new MySQLConnector();
169
- ConnectorRegistry.register(mysqlConnector);
@@ -1,172 +0,0 @@
1
- import pg from 'pg';
2
- const { Pool } = pg;
3
- import { ConnectorRegistry } from '../interface.js';
4
- /**
5
- * PostgreSQL DSN Parser
6
- * Handles DSN strings like: postgres://user:password@localhost:5432/dbname?sslmode=disable
7
- */
8
- class PostgresDSNParser {
9
- parse(dsn) {
10
- // Basic validation
11
- if (!this.isValidDSN(dsn)) {
12
- throw new Error(`Invalid PostgreSQL DSN: ${dsn}`);
13
- }
14
- try {
15
- // For PostgreSQL, we can actually pass the DSN directly to the Pool constructor
16
- // But we'll parse it here for consistency and to extract components if needed
17
- const url = new URL(dsn);
18
- const config = {
19
- host: url.hostname,
20
- port: url.port ? parseInt(url.port) : 5432,
21
- database: url.pathname.substring(1), // Remove leading '/'
22
- user: url.username,
23
- password: url.password,
24
- };
25
- // Handle query parameters (like sslmode, etc.)
26
- url.searchParams.forEach((value, key) => {
27
- if (key === 'sslmode') {
28
- config.ssl = value !== 'disable';
29
- }
30
- // Add other parameters as needed
31
- });
32
- return config;
33
- }
34
- catch (error) {
35
- throw new Error(`Failed to parse PostgreSQL DSN: ${error instanceof Error ? error.message : String(error)}`);
36
- }
37
- }
38
- getSampleDSN() {
39
- return 'postgres://postgres:password@localhost:5432/postgres?sslmode=disable';
40
- }
41
- isValidDSN(dsn) {
42
- try {
43
- const url = new URL(dsn);
44
- return url.protocol === 'postgres:' || url.protocol === 'postgresql:';
45
- }
46
- catch (error) {
47
- return false;
48
- }
49
- }
50
- }
51
- /**
52
- * PostgreSQL Connector Implementation
53
- */
54
- export class PostgresConnector {
55
- constructor() {
56
- this.id = 'postgres';
57
- this.name = 'PostgreSQL';
58
- this.dsnParser = new PostgresDSNParser();
59
- this.pool = null;
60
- }
61
- async connect(dsn) {
62
- try {
63
- const config = this.dsnParser.parse(dsn);
64
- this.pool = new Pool(config);
65
- // Test the connection
66
- const client = await this.pool.connect();
67
- console.error("Successfully connected to PostgreSQL database");
68
- client.release();
69
- }
70
- catch (err) {
71
- console.error("Failed to connect to PostgreSQL database:", err);
72
- throw err;
73
- }
74
- }
75
- async disconnect() {
76
- if (this.pool) {
77
- await this.pool.end();
78
- this.pool = null;
79
- }
80
- }
81
- async getTables() {
82
- if (!this.pool) {
83
- throw new Error('Not connected to database');
84
- }
85
- const client = await this.pool.connect();
86
- try {
87
- const result = await client.query(`
88
- SELECT table_name
89
- FROM information_schema.tables
90
- WHERE table_schema = 'public'
91
- ORDER BY table_name
92
- `);
93
- return result.rows.map(row => row.table_name);
94
- }
95
- finally {
96
- client.release();
97
- }
98
- }
99
- async tableExists(tableName) {
100
- if (!this.pool) {
101
- throw new Error('Not connected to database');
102
- }
103
- const client = await this.pool.connect();
104
- try {
105
- const result = await client.query(`
106
- SELECT EXISTS (
107
- SELECT FROM information_schema.tables
108
- WHERE table_schema = 'public'
109
- AND table_name = $1
110
- )
111
- `, [tableName]);
112
- return result.rows[0].exists;
113
- }
114
- finally {
115
- client.release();
116
- }
117
- }
118
- async getTableSchema(tableName) {
119
- if (!this.pool) {
120
- throw new Error('Not connected to database');
121
- }
122
- const client = await this.pool.connect();
123
- try {
124
- // Get table columns
125
- const result = await client.query(`
126
- SELECT
127
- column_name,
128
- data_type,
129
- is_nullable,
130
- column_default
131
- FROM information_schema.columns
132
- WHERE table_schema = 'public'
133
- AND table_name = $1
134
- ORDER BY ordinal_position
135
- `, [tableName]);
136
- return result.rows;
137
- }
138
- finally {
139
- client.release();
140
- }
141
- }
142
- async executeQuery(query) {
143
- if (!this.pool) {
144
- throw new Error('Not connected to database');
145
- }
146
- const safetyCheck = this.validateQuery(query);
147
- if (!safetyCheck.isValid) {
148
- throw new Error(safetyCheck.message || "Query validation failed");
149
- }
150
- const client = await this.pool.connect();
151
- try {
152
- return await client.query(query);
153
- }
154
- finally {
155
- client.release();
156
- }
157
- }
158
- validateQuery(query) {
159
- // Basic check to prevent non-SELECT queries
160
- const normalizedQuery = query.trim().toLowerCase();
161
- if (!normalizedQuery.startsWith('select')) {
162
- return {
163
- isValid: false,
164
- message: "Only SELECT queries are allowed for security reasons."
165
- };
166
- }
167
- return { isValid: true };
168
- }
169
- }
170
- // Create and register the connector
171
- const postgresConnector = new PostgresConnector();
172
- ConnectorRegistry.register(postgresConnector);
@@ -1,208 +0,0 @@
1
- /**
2
- * SQLite Connector Implementation
3
- *
4
- * Implements SQLite database connectivity for DBHub
5
- * To use this connector:
6
- * 1. Set DSN=sqlite:///path/to/database.db in your .env file
7
- * 2. Or set DB_CONNECTOR_TYPE=sqlite for default in-memory database
8
- */
9
- import { ConnectorRegistry } from '../interface.js';
10
- import sqlite3 from 'sqlite3';
11
- /**
12
- * SQLite DSN Parser
13
- * Handles DSN strings like:
14
- * - sqlite:///path/to/database.db (absolute path)
15
- * - sqlite://./relative/path/to/database.db (relative path)
16
- * - sqlite::memory: (in-memory database)
17
- */
18
- class SQLiteDSNParser {
19
- parse(dsn) {
20
- // Basic validation
21
- if (!this.isValidDSN(dsn)) {
22
- throw new Error(`Invalid SQLite DSN: ${dsn}`);
23
- }
24
- try {
25
- const url = new URL(dsn);
26
- let dbPath;
27
- // Handle in-memory database
28
- if (url.hostname === '' && url.pathname === ':memory:') {
29
- dbPath = ':memory:';
30
- }
31
- // Handle file paths
32
- else {
33
- // Get the path part, handling both relative and absolute paths
34
- if (url.pathname.startsWith('//')) {
35
- // Absolute path: sqlite:///path/to/db.sqlite
36
- dbPath = url.pathname.substring(2); // Remove leading //
37
- }
38
- else {
39
- // Relative path: sqlite://./path/to/db.sqlite
40
- dbPath = url.pathname;
41
- }
42
- }
43
- return { dbPath };
44
- }
45
- catch (error) {
46
- throw new Error(`Failed to parse SQLite DSN: ${error instanceof Error ? error.message : String(error)}`);
47
- }
48
- }
49
- getSampleDSN() {
50
- return 'sqlite:///path/to/database.db';
51
- }
52
- isValidDSN(dsn) {
53
- try {
54
- const url = new URL(dsn);
55
- return url.protocol === 'sqlite:';
56
- }
57
- catch (error) {
58
- return false;
59
- }
60
- }
61
- }
62
- export class SQLiteConnector {
63
- constructor() {
64
- this.id = 'sqlite';
65
- this.name = 'SQLite';
66
- this.dsnParser = new SQLiteDSNParser();
67
- this.db = null;
68
- this.dbPath = ':memory:'; // Default to in-memory database
69
- }
70
- async connect(dsn, initScript) {
71
- const config = this.dsnParser.parse(dsn);
72
- this.dbPath = config.dbPath;
73
- return new Promise((resolve, reject) => {
74
- this.db = new sqlite3.Database(this.dbPath, (err) => {
75
- if (err) {
76
- console.error("Failed to connect to SQLite database:", err);
77
- reject(err);
78
- }
79
- else {
80
- // Can't use console.log here because it will break the stdio transport
81
- console.error("Successfully connected to SQLite database");
82
- // If an initialization script is provided, run it
83
- if (initScript) {
84
- this.db.exec(initScript, (err) => {
85
- if (err) {
86
- console.error("Failed to initialize database with script:", err);
87
- reject(err);
88
- }
89
- else {
90
- console.error("Successfully initialized database with script");
91
- resolve();
92
- }
93
- });
94
- }
95
- else {
96
- resolve();
97
- }
98
- }
99
- });
100
- });
101
- }
102
- async disconnect() {
103
- // Close the SQLite connection
104
- if (this.db) {
105
- return new Promise((resolve, reject) => {
106
- this.db.close((err) => {
107
- if (err) {
108
- reject(err);
109
- }
110
- else {
111
- this.db = null;
112
- resolve();
113
- }
114
- });
115
- });
116
- }
117
- return Promise.resolve();
118
- }
119
- async getTables() {
120
- if (!this.db) {
121
- throw new Error("Not connected to SQLite database");
122
- }
123
- return new Promise((resolve, reject) => {
124
- this.db.all(`SELECT name FROM sqlite_master
125
- WHERE type='table' AND name NOT LIKE 'sqlite_%'
126
- ORDER BY name`, (err, rows) => {
127
- if (err) {
128
- reject(err);
129
- }
130
- else {
131
- resolve(rows.map(row => row.name));
132
- }
133
- });
134
- });
135
- }
136
- async tableExists(tableName) {
137
- if (!this.db) {
138
- throw new Error("Not connected to SQLite database");
139
- }
140
- return new Promise((resolve, reject) => {
141
- this.db.get(`SELECT name FROM sqlite_master
142
- WHERE type='table' AND name = ?`, [tableName], (err, row) => {
143
- if (err) {
144
- reject(err);
145
- }
146
- else {
147
- resolve(!!row);
148
- }
149
- });
150
- });
151
- }
152
- async getTableSchema(tableName) {
153
- if (!this.db) {
154
- throw new Error("Not connected to SQLite database");
155
- }
156
- return new Promise((resolve, reject) => {
157
- this.db.all(`PRAGMA table_info(${tableName})`, (err, rows) => {
158
- if (err) {
159
- reject(err);
160
- }
161
- else {
162
- // Convert SQLite schema format to our standard TableColumn format
163
- const columns = rows.map(row => ({
164
- column_name: row.name,
165
- data_type: row.type,
166
- is_nullable: row.notnull === 0 ? 'YES' : 'NO', // In SQLite, 0 means nullable
167
- column_default: row.dflt_value
168
- }));
169
- resolve(columns);
170
- }
171
- });
172
- });
173
- }
174
- async executeQuery(query) {
175
- if (!this.db) {
176
- throw new Error("Not connected to SQLite database");
177
- }
178
- // Validate query for safety
179
- const safetyCheck = this.validateQuery(query);
180
- if (!safetyCheck.isValid) {
181
- throw new Error(safetyCheck.message || "Query validation failed");
182
- }
183
- return new Promise((resolve, reject) => {
184
- this.db.all(query, (err, rows) => {
185
- if (err) {
186
- reject(err);
187
- }
188
- else {
189
- resolve({ rows });
190
- }
191
- });
192
- });
193
- }
194
- validateQuery(query) {
195
- // Basic check to prevent non-SELECT queries
196
- const normalizedQuery = query.trim().toLowerCase();
197
- if (!normalizedQuery.startsWith('select')) {
198
- return {
199
- isValid: false,
200
- message: "Only SELECT queries are allowed for security reasons."
201
- };
202
- }
203
- return { isValid: true };
204
- }
205
- }
206
- // Register the SQLite connector
207
- const sqliteConnector = new SQLiteConnector();
208
- ConnectorRegistry.register(sqliteConnector);