Skip to content

Commit 9fde281

Browse files
committed
chore: use better-sqlite3
1 parent 1e7de12 commit 9fde281

File tree

3 files changed

+87
-98
lines changed

3 files changed

+87
-98
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@
2323
"license": "MIT",
2424
"dependencies": {
2525
"@modelcontextprotocol/sdk": "^1.6.1",
26-
"@types/sqlite3": "^5.1.0",
26+
"better-sqlite3": "^11.9.0",
2727
"dotenv": "^16.4.7",
2828
"express": "^4.18.2",
2929
"mssql": "^11.0.1",
3030
"mysql2": "^3.13.0",
3131
"pg": "^8.13.3",
32-
"sqlite3": "^5.1.7",
3332
"zod": "^3.24.2"
3433
},
3534
"devDependencies": {
35+
"@types/better-sqlite3": "^7.6.12",
3636
"@types/express": "^4.17.21",
3737
"@types/mssql": "^9.1.7",
3838
"@types/node": "^22.13.10",

pnpm-lock.yaml

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/connectors/sqlite/index.ts

Lines changed: 64 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/**
22
* SQLite Connector Implementation
33
*
4-
* Implements SQLite database connectivity for DBHub
4+
* Implements SQLite database connectivity for DBHub using better-sqlite3
55
* To use this connector:
66
* 1. Set DSN=sqlite:///path/to/database.db in your .env file
77
* 2. Or set DB_CONNECTOR_TYPE=sqlite for default in-memory database
88
*/
99

1010
import { Connector, ConnectorRegistry, DSNParser, QueryResult, TableColumn } from '../interface.js';
11-
import sqlite3 from 'sqlite3';
11+
import Database from 'better-sqlite3';
1212

1313
/**
1414
* SQLite DSN Parser
@@ -81,54 +81,36 @@ export class SQLiteConnector implements Connector {
8181
name = 'SQLite';
8282
dsnParser = new SQLiteDSNParser();
8383

84-
private db: sqlite3.Database | null = null;
84+
private db: Database.Database | null = null;
8585
private dbPath: string = ':memory:'; // Default to in-memory database
8686

8787
async connect(dsn: string, initScript?: string): Promise<void> {
8888
const config = this.dsnParser.parse(dsn);
8989
this.dbPath = config.dbPath;
9090

91-
return new Promise((resolve, reject) => {
92-
this.db = new sqlite3.Database(this.dbPath, (err) => {
93-
if (err) {
94-
console.error("Failed to connect to SQLite database:", err);
95-
reject(err);
96-
} else {
97-
// Can't use console.log here because it will break the stdio transport
98-
console.error("Successfully connected to SQLite database");
99-
100-
// If an initialization script is provided, run it
101-
if (initScript) {
102-
this.db!.exec(initScript, (err) => {
103-
if (err) {
104-
console.error("Failed to initialize database with script:", err);
105-
reject(err);
106-
} else {
107-
console.error("Successfully initialized database with script");
108-
resolve();
109-
}
110-
});
111-
} else {
112-
resolve();
113-
}
114-
}
115-
});
116-
});
91+
try {
92+
this.db = new Database(this.dbPath);
93+
console.error("Successfully connected to SQLite database");
94+
95+
// If an initialization script is provided, run it
96+
if (initScript) {
97+
this.db.exec(initScript);
98+
console.error("Successfully initialized database with script");
99+
}
100+
} catch (error) {
101+
console.error("Failed to connect to SQLite database:", error);
102+
throw error;
103+
}
117104
}
118105

119106
async disconnect(): Promise<void> {
120-
// Close the SQLite connection
121107
if (this.db) {
122-
return new Promise((resolve, reject) => {
123-
this.db!.close((err) => {
124-
if (err) {
125-
reject(err);
126-
} else {
127-
this.db = null;
128-
resolve();
129-
}
130-
});
131-
});
108+
try {
109+
this.db.close();
110+
this.db = null;
111+
} catch (error) {
112+
throw error;
113+
}
132114
}
133115
return Promise.resolve();
134116
}
@@ -138,67 +120,56 @@ export class SQLiteConnector implements Connector {
138120
throw new Error("Not connected to SQLite database");
139121
}
140122

141-
return new Promise((resolve, reject) => {
142-
this.db!.all<SQLiteTableNameRow>(
143-
`SELECT name FROM sqlite_master
144-
WHERE type='table' AND name NOT LIKE 'sqlite_%'
145-
ORDER BY name`,
146-
(err, rows) => {
147-
if (err) {
148-
reject(err);
149-
} else {
150-
resolve(rows.map(row => row.name));
151-
}
152-
}
153-
);
154-
});
123+
try {
124+
const rows = this.db.prepare(`
125+
SELECT name FROM sqlite_master
126+
WHERE type='table' AND name NOT LIKE 'sqlite_%'
127+
ORDER BY name
128+
`).all() as SQLiteTableNameRow[];
129+
130+
return rows.map(row => row.name);
131+
} catch (error) {
132+
throw error;
133+
}
155134
}
156135

157136
async tableExists(tableName: string): Promise<boolean> {
158137
if (!this.db) {
159138
throw new Error("Not connected to SQLite database");
160139
}
161140

162-
return new Promise((resolve, reject) => {
163-
this.db!.get<SQLiteTableNameRow>(
164-
`SELECT name FROM sqlite_master
165-
WHERE type='table' AND name = ?`,
166-
[tableName],
167-
(err, row) => {
168-
if (err) {
169-
reject(err);
170-
} else {
171-
resolve(!!row);
172-
}
173-
}
174-
);
175-
});
141+
try {
142+
const row = this.db.prepare(`
143+
SELECT name FROM sqlite_master
144+
WHERE type='table' AND name = ?
145+
`).get(tableName) as SQLiteTableNameRow | undefined;
146+
147+
return !!row;
148+
} catch (error) {
149+
throw error;
150+
}
176151
}
177152

178153
async getTableSchema(tableName: string): Promise<TableColumn[]> {
179154
if (!this.db) {
180155
throw new Error("Not connected to SQLite database");
181156
}
182157

183-
return new Promise((resolve, reject) => {
184-
this.db!.all<SQLiteTableInfo>(
185-
`PRAGMA table_info(${tableName})`,
186-
(err, rows) => {
187-
if (err) {
188-
reject(err);
189-
} else {
190-
// Convert SQLite schema format to our standard TableColumn format
191-
const columns = rows.map(row => ({
192-
column_name: row.name,
193-
data_type: row.type,
194-
is_nullable: row.notnull === 0 ? 'YES' : 'NO', // In SQLite, 0 means nullable
195-
column_default: row.dflt_value
196-
}));
197-
resolve(columns);
198-
}
199-
}
200-
);
201-
});
158+
try {
159+
const rows = this.db.prepare(`PRAGMA table_info(${tableName})`).all() as SQLiteTableInfo[];
160+
161+
// Convert SQLite schema format to our standard TableColumn format
162+
const columns = rows.map(row => ({
163+
column_name: row.name,
164+
data_type: row.type,
165+
is_nullable: row.notnull === 0 ? 'YES' : 'NO', // In SQLite, 0 means nullable
166+
column_default: row.dflt_value
167+
}));
168+
169+
return columns;
170+
} catch (error) {
171+
throw error;
172+
}
202173
}
203174

204175
async executeQuery(query: string): Promise<QueryResult> {
@@ -212,15 +183,12 @@ export class SQLiteConnector implements Connector {
212183
throw new Error(safetyCheck.message || "Query validation failed");
213184
}
214185

215-
return new Promise((resolve, reject) => {
216-
this.db!.all(query, (err, rows) => {
217-
if (err) {
218-
reject(err);
219-
} else {
220-
resolve({ rows });
221-
}
222-
});
223-
});
186+
try {
187+
const rows = this.db.prepare(query).all();
188+
return { rows };
189+
} catch (error) {
190+
throw error;
191+
}
224192
}
225193

226194
validateQuery(query: string): { isValid: boolean; message?: string } {

0 commit comments

Comments
 (0)