@xcr1234/dbhub-fork 1.0.0 → 1.1.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/dist/{chunk-6O5I6UAW.js → chunk-YYDWHEUE.js} +1 -1
- package/dist/{demo-loader-WKQAEFSX.js → demo-loader-PSMTLZ2T.js} +0 -2
- package/dist/index.js +8 -9
- package/dist/{mariadb-QHRMVK47.js → mariadb-4IBCPIEV.js} +0 -1
- package/dist/{mysql-GOLPG2Q6.js → mysql-M4INQQ46.js} +0 -1
- package/dist/oracle-N4YKSM4F.js +384 -0
- package/dist/{postgres-CK6N5BXI.js → postgres-SN3BESYN.js} +0 -1
- package/dist/{registry-KI3KJMRY.js → registry-H3S7N4PP.js} +1 -2
- package/dist/sqlite-SJAXGYH3.js +321 -0
- package/dist/{sqlserver-5RM44GWI.js → sqlserver-YV3KVJ3B.js} +0 -1
- package/package.json +1 -1
- package/dist/chunk-FAIJPBT5.js +0 -40
- package/dist/oracle-3V2T7ZWF.js +0 -27739
- package/dist/sqlite-P6NXTMYE.js +0 -2473
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import {
|
|
2
|
+
quoteIdentifier
|
|
3
|
+
} from "./chunk-JFWX35TB.js";
|
|
4
|
+
import {
|
|
5
|
+
SQLRowLimiter
|
|
6
|
+
} from "./chunk-BRXZ5ZQB.js";
|
|
7
|
+
import {
|
|
8
|
+
ConnectorRegistry,
|
|
9
|
+
SafeURL,
|
|
10
|
+
obfuscateDSNPassword,
|
|
11
|
+
splitSQLStatements
|
|
12
|
+
} from "./chunk-C7WEAPX4.js";
|
|
13
|
+
|
|
14
|
+
// src/connectors/sqlite/index.ts
|
|
15
|
+
import fs from "fs/promises";
|
|
16
|
+
import initSqlJs from "sql.js";
|
|
17
|
+
var SQLiteDSNParser = class {
|
|
18
|
+
async parse(dsn, config) {
|
|
19
|
+
if (!this.isValidDSN(dsn)) {
|
|
20
|
+
const obfuscatedDSN = obfuscateDSNPassword(dsn);
|
|
21
|
+
const expectedFormat = this.getSampleDSN();
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Invalid SQLite DSN format.
|
|
24
|
+
Provided: ${obfuscatedDSN}
|
|
25
|
+
Expected: ${expectedFormat}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const url = new SafeURL(dsn);
|
|
30
|
+
let dbPath;
|
|
31
|
+
if (url.hostname === "" && url.pathname === "/:memory:") {
|
|
32
|
+
dbPath = ":memory:";
|
|
33
|
+
} else {
|
|
34
|
+
if (url.pathname.startsWith("//")) {
|
|
35
|
+
dbPath = url.pathname.substring(2);
|
|
36
|
+
} else if (url.pathname.match(/^\/[A-Za-z]:\//)) {
|
|
37
|
+
dbPath = url.pathname.substring(1);
|
|
38
|
+
} else {
|
|
39
|
+
dbPath = url.pathname;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { dbPath };
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Failed to parse SQLite DSN: ${error instanceof Error ? error.message : String(error)}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
getSampleDSN() {
|
|
50
|
+
return "sqlite:///path/to/database.db";
|
|
51
|
+
}
|
|
52
|
+
isValidDSN(dsn) {
|
|
53
|
+
try {
|
|
54
|
+
return dsn.startsWith("sqlite://");
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var SQLiteConnector = class _SQLiteConnector {
|
|
61
|
+
constructor() {
|
|
62
|
+
this.id = "sqlite";
|
|
63
|
+
this.name = "SQLite";
|
|
64
|
+
this.dsnParser = new SQLiteDSNParser();
|
|
65
|
+
this.db = null;
|
|
66
|
+
this.dbPath = ":memory:";
|
|
67
|
+
this.SQL = null;
|
|
68
|
+
this.sourceId = "default";
|
|
69
|
+
}
|
|
70
|
+
getId() {
|
|
71
|
+
return this.sourceId;
|
|
72
|
+
}
|
|
73
|
+
clone() {
|
|
74
|
+
return new _SQLiteConnector();
|
|
75
|
+
}
|
|
76
|
+
// Helper method to save in-memory database to disk
|
|
77
|
+
async saveToDisk() {
|
|
78
|
+
if (this.dbPath !== ":memory:" && this.db) {
|
|
79
|
+
const data = this.db.export();
|
|
80
|
+
const buffer = Buffer.from(data);
|
|
81
|
+
await fs.writeFile(this.dbPath, buffer);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Wrapper equivalent to sqlite3 db.all()
|
|
85
|
+
async dbAll(sql, params = []) {
|
|
86
|
+
if (!this.db) throw new Error("Not connected to SQLite database");
|
|
87
|
+
const stmt = this.db.prepare(sql);
|
|
88
|
+
try {
|
|
89
|
+
if (params && params.length > 0) {
|
|
90
|
+
stmt.bind(params);
|
|
91
|
+
}
|
|
92
|
+
const rows = [];
|
|
93
|
+
while (stmt.step()) {
|
|
94
|
+
rows.push(stmt.getAsObject());
|
|
95
|
+
}
|
|
96
|
+
return rows;
|
|
97
|
+
} finally {
|
|
98
|
+
stmt.free();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Wrapper equivalent to sqlite3 db.get()
|
|
102
|
+
async dbGet(sql, params = []) {
|
|
103
|
+
const rows = await this.dbAll(sql, params);
|
|
104
|
+
return rows.length > 0 ? rows[0] : void 0;
|
|
105
|
+
}
|
|
106
|
+
// Wrapper equivalent to sqlite3 db.run()
|
|
107
|
+
async dbRun(sql, params = []) {
|
|
108
|
+
if (!this.db) throw new Error("Not connected to SQLite database");
|
|
109
|
+
this.db.run(sql, params);
|
|
110
|
+
return { changes: this.db.getRowsModified() };
|
|
111
|
+
}
|
|
112
|
+
async connect(dsn, initScript, config) {
|
|
113
|
+
const parsedConfig = await this.dsnParser.parse(dsn, config);
|
|
114
|
+
this.dbPath = parsedConfig.dbPath;
|
|
115
|
+
try {
|
|
116
|
+
if (!this.SQL) {
|
|
117
|
+
this.SQL = await initSqlJs();
|
|
118
|
+
}
|
|
119
|
+
if (this.dbPath === ":memory:") {
|
|
120
|
+
this.db = new this.SQL.Database();
|
|
121
|
+
} else {
|
|
122
|
+
try {
|
|
123
|
+
const fileBuffer = await fs.readFile(this.dbPath);
|
|
124
|
+
this.db = new this.SQL.Database(fileBuffer);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (err.code === "ENOENT") {
|
|
127
|
+
if (config?.readonly) {
|
|
128
|
+
throw new Error(`Database file not found in readonly mode: ${this.dbPath}`);
|
|
129
|
+
}
|
|
130
|
+
this.db = new this.SQL.Database();
|
|
131
|
+
await this.saveToDisk();
|
|
132
|
+
} else {
|
|
133
|
+
throw err;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (initScript) {
|
|
138
|
+
this.db.run(initScript);
|
|
139
|
+
await this.saveToDisk();
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error("Failed to connect to SQLite database (sql.js):", error);
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async disconnect() {
|
|
147
|
+
if (this.db) {
|
|
148
|
+
try {
|
|
149
|
+
await this.saveToDisk();
|
|
150
|
+
this.db.close();
|
|
151
|
+
this.db = null;
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error("Error during SQLite disconnect:", error);
|
|
154
|
+
this.db = null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return Promise.resolve();
|
|
158
|
+
}
|
|
159
|
+
async getSchemas() {
|
|
160
|
+
if (!this.db) throw new Error("Not connected to SQLite database");
|
|
161
|
+
return ["main"];
|
|
162
|
+
}
|
|
163
|
+
async getTables(schema) {
|
|
164
|
+
try {
|
|
165
|
+
const rows = await this.dbAll(`
|
|
166
|
+
SELECT name FROM sqlite_master
|
|
167
|
+
WHERE type='table' AND name NOT LIKE 'sqlite_%'
|
|
168
|
+
ORDER BY name
|
|
169
|
+
`);
|
|
170
|
+
return rows.map((row) => row.name);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async tableExists(tableName, schema) {
|
|
176
|
+
try {
|
|
177
|
+
const row = await this.dbGet(`
|
|
178
|
+
SELECT name FROM sqlite_master
|
|
179
|
+
WHERE type='table' AND name = ?
|
|
180
|
+
`, [tableName]);
|
|
181
|
+
return !!row;
|
|
182
|
+
} catch (error) {
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async getTableIndexes(tableName, schema) {
|
|
187
|
+
try {
|
|
188
|
+
const indexInfoRows = await this.dbAll(`
|
|
189
|
+
SELECT name as index_name, 0 as is_unique
|
|
190
|
+
FROM sqlite_master
|
|
191
|
+
WHERE type = 'index' AND tbl_name = ?
|
|
192
|
+
`, [tableName]);
|
|
193
|
+
const quotedTableName = quoteIdentifier(tableName, "sqlite");
|
|
194
|
+
const indexListRows = await this.dbAll(
|
|
195
|
+
`PRAGMA index_list(${quotedTableName})`
|
|
196
|
+
);
|
|
197
|
+
const indexUniqueMap = /* @__PURE__ */ new Map();
|
|
198
|
+
for (const indexListRow of indexListRows) {
|
|
199
|
+
indexUniqueMap.set(indexListRow.name, indexListRow.unique === 1);
|
|
200
|
+
}
|
|
201
|
+
const tableInfo = await this.dbAll(
|
|
202
|
+
`PRAGMA table_info(${quotedTableName})`
|
|
203
|
+
);
|
|
204
|
+
const pkColumns = tableInfo.filter((col) => col.pk > 0).map((col) => col.name);
|
|
205
|
+
const results = [];
|
|
206
|
+
for (const indexInfo of indexInfoRows) {
|
|
207
|
+
const quotedIndexName = quoteIdentifier(indexInfo.index_name, "sqlite");
|
|
208
|
+
const indexDetailRows = await this.dbAll(
|
|
209
|
+
`PRAGMA index_info(${quotedIndexName})`
|
|
210
|
+
);
|
|
211
|
+
const columnNames = indexDetailRows.map((row) => row.name);
|
|
212
|
+
results.push({
|
|
213
|
+
index_name: indexInfo.index_name,
|
|
214
|
+
column_names: columnNames,
|
|
215
|
+
is_unique: indexUniqueMap.get(indexInfo.index_name) || false,
|
|
216
|
+
is_primary: false
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (pkColumns.length > 0) {
|
|
220
|
+
results.push({
|
|
221
|
+
index_name: "PRIMARY",
|
|
222
|
+
column_names: pkColumns,
|
|
223
|
+
is_unique: true,
|
|
224
|
+
is_primary: true
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return results;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async getTableSchema(tableName, schema) {
|
|
233
|
+
try {
|
|
234
|
+
const quotedTableName = quoteIdentifier(tableName, "sqlite");
|
|
235
|
+
const rows = await this.dbAll(`PRAGMA table_info(${quotedTableName})`);
|
|
236
|
+
return rows.map((row) => ({
|
|
237
|
+
column_name: row.name,
|
|
238
|
+
data_type: row.type,
|
|
239
|
+
is_nullable: row.notnull === 1 || row.pk > 0 ? "NO" : "YES",
|
|
240
|
+
column_default: row.dflt_value,
|
|
241
|
+
description: null
|
|
242
|
+
}));
|
|
243
|
+
} catch (error) {
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async getStoredProcedures(schema, routineType) {
|
|
248
|
+
if (!this.db) throw new Error("Not connected to SQLite database");
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
async getStoredProcedureDetail(procedureName, schema) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
"SQLite does not support stored procedures. Functions are defined programmatically through the SQLite API, not stored in the database."
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
async executeSQL(sql, options, parameters) {
|
|
257
|
+
if (!this.db) throw new Error("Not connected to SQLite database");
|
|
258
|
+
try {
|
|
259
|
+
const statements = splitSQLStatements(sql, "sqlite");
|
|
260
|
+
let dataModified = false;
|
|
261
|
+
if (statements.length === 1) {
|
|
262
|
+
let processedStatement = statements[0];
|
|
263
|
+
const trimmedStatement = statements[0].toLowerCase().trim();
|
|
264
|
+
const isReadStatement = trimmedStatement.startsWith("select") || trimmedStatement.startsWith("with") || trimmedStatement.startsWith("explain") || trimmedStatement.startsWith("analyze") || trimmedStatement.startsWith("pragma") && (trimmedStatement.includes("table_info") || trimmedStatement.includes("index_info") || trimmedStatement.includes("index_list") || trimmedStatement.includes("foreign_key_list"));
|
|
265
|
+
if (options.maxRows) {
|
|
266
|
+
processedStatement = SQLRowLimiter.applyMaxRows(processedStatement, options.maxRows);
|
|
267
|
+
}
|
|
268
|
+
if (isReadStatement) {
|
|
269
|
+
try {
|
|
270
|
+
const params = parameters || [];
|
|
271
|
+
const rows = await this.dbAll(processedStatement, params);
|
|
272
|
+
return { rows, rowCount: rows.length };
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.error(`[SQLite executeSQL] ERROR: ${error.message}`);
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
try {
|
|
279
|
+
const params = parameters || [];
|
|
280
|
+
const result = await this.dbRun(processedStatement, params);
|
|
281
|
+
await this.saveToDisk();
|
|
282
|
+
return { rows: [], rowCount: result.changes ?? 0 };
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error(`[SQLite executeSQL] ERROR: ${error.message}`);
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
if (parameters && parameters.length > 0) {
|
|
290
|
+
throw new Error("Parameters are not supported for multi-statement queries in SQLite");
|
|
291
|
+
}
|
|
292
|
+
let totalChanges = 0;
|
|
293
|
+
let allRows = [];
|
|
294
|
+
for (let statement of statements) {
|
|
295
|
+
const trimmedStatement = statement.toLowerCase().trim();
|
|
296
|
+
const isReadStatement = trimmedStatement.startsWith("select") || trimmedStatement.startsWith("with") || trimmedStatement.startsWith("explain") || trimmedStatement.startsWith("analyze") || trimmedStatement.startsWith("pragma") && (trimmedStatement.includes("table_info") || trimmedStatement.includes("index_info") || trimmedStatement.includes("index_list") || trimmedStatement.includes("foreign_key_list"));
|
|
297
|
+
if (isReadStatement) {
|
|
298
|
+
statement = SQLRowLimiter.applyMaxRows(statement, options.maxRows);
|
|
299
|
+
const rows = await this.dbAll(statement);
|
|
300
|
+
allRows.push(...rows);
|
|
301
|
+
} else {
|
|
302
|
+
const result = await this.dbRun(statement);
|
|
303
|
+
totalChanges += result.changes ?? 0;
|
|
304
|
+
dataModified = true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (dataModified) {
|
|
308
|
+
await this.saveToDisk();
|
|
309
|
+
}
|
|
310
|
+
return { rows: allRows, rowCount: totalChanges + allRows.length };
|
|
311
|
+
}
|
|
312
|
+
} catch (error) {
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
var sqliteConnector = new SQLiteConnector();
|
|
318
|
+
ConnectorRegistry.register(sqliteConnector);
|
|
319
|
+
export {
|
|
320
|
+
SQLiteConnector
|
|
321
|
+
};
|
package/package.json
CHANGED
package/dist/chunk-FAIJPBT5.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
-
}) : x)(function(x) {
|
|
11
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
-
});
|
|
14
|
-
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
-
};
|
|
17
|
-
var __copyProps = (to, from, except, desc) => {
|
|
18
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
-
for (let key of __getOwnPropNames(from))
|
|
20
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
-
}
|
|
23
|
-
return to;
|
|
24
|
-
};
|
|
25
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
-
mod
|
|
32
|
-
));
|
|
33
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
34
|
-
|
|
35
|
-
export {
|
|
36
|
-
__require,
|
|
37
|
-
__commonJS,
|
|
38
|
-
__toESM,
|
|
39
|
-
__publicField
|
|
40
|
-
};
|