fdb2 1.0.38 → 1.0.40

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 (64) hide show
  1. package/README.md +8 -1
  2. package/bin/fdb2.js +78 -3
  3. package/dist/public/explorer.css +403 -380
  4. package/dist/public/explorer.js +1294 -737
  5. package/dist/public/index.js +242 -16
  6. package/dist/public/layout.js +1 -1
  7. package/dist/scripts/verify-db-support.cjs +239 -0
  8. package/dist/server/core/dialect.d.ts +9 -0
  9. package/dist/server/core/dialect.d.ts.map +1 -1
  10. package/dist/server/core/dialect.js +24 -9
  11. package/dist/server/core/dialect.js.map +1 -1
  12. package/dist/server/core/dialect.ts +29 -9
  13. package/dist/server/core/schema/__tests__/ddl-emitter.test.ts +7 -3
  14. package/dist/server/core/schema/ddl-emitter.d.ts.map +1 -1
  15. package/dist/server/core/schema/ddl-emitter.js +32 -12
  16. package/dist/server/core/schema/ddl-emitter.js.map +1 -1
  17. package/dist/server/core/schema/ddl-emitter.ts +34 -11
  18. package/dist/server/index.d.ts.map +1 -1
  19. package/dist/server/index.js +83 -6
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.ts +87 -6
  22. package/dist/server/model/ddl-spec.d.ts +9 -0
  23. package/dist/server/model/ddl-spec.d.ts.map +1 -1
  24. package/dist/server/model/ddl-spec.ts +9 -0
  25. package/dist/server/service/connection.service.d.ts +8 -0
  26. package/dist/server/service/connection.service.d.ts.map +1 -1
  27. package/dist/server/service/connection.service.js +75 -17
  28. package/dist/server/service/connection.service.js.map +1 -1
  29. package/dist/server/service/connection.service.ts +74 -17
  30. package/dist/server/service/database/base.service.d.ts +45 -0
  31. package/dist/server/service/database/base.service.d.ts.map +1 -1
  32. package/dist/server/service/database/base.service.js +120 -7
  33. package/dist/server/service/database/base.service.js.map +1 -1
  34. package/dist/server/service/database/base.service.ts +143 -8
  35. package/dist/server/service/database/cockroachdb.service.d.ts +9 -0
  36. package/dist/server/service/database/cockroachdb.service.d.ts.map +1 -1
  37. package/dist/server/service/database/cockroachdb.service.js +67 -56
  38. package/dist/server/service/database/cockroachdb.service.js.map +1 -1
  39. package/dist/server/service/database/cockroachdb.service.ts +66 -57
  40. package/dist/server/service/database/database.service.d.ts +3 -221
  41. package/dist/server/service/database/database.service.d.ts.map +1 -1
  42. package/dist/server/service/database/database.service.js +66 -24
  43. package/dist/server/service/database/database.service.js.map +1 -1
  44. package/dist/server/service/database/database.service.ts +69 -23
  45. package/dist/server/service/database/mongodb.service.d.ts +58 -3
  46. package/dist/server/service/database/mongodb.service.d.ts.map +1 -1
  47. package/dist/server/service/database/mongodb.service.js +251 -54
  48. package/dist/server/service/database/mongodb.service.js.map +1 -1
  49. package/dist/server/service/database/mongodb.service.ts +275 -60
  50. package/dist/server/service/database/mssql.service.d.ts +8 -0
  51. package/dist/server/service/database/mssql.service.d.ts.map +1 -1
  52. package/dist/server/service/database/mssql.service.js +41 -36
  53. package/dist/server/service/database/mssql.service.js.map +1 -1
  54. package/dist/server/service/database/mssql.service.ts +45 -40
  55. package/dist/server/service/database/oracle.service.d.ts +24 -0
  56. package/dist/server/service/database/oracle.service.d.ts.map +1 -1
  57. package/dist/server/service/database/oracle.service.js +146 -106
  58. package/dist/server/service/database/oracle.service.js.map +1 -1
  59. package/dist/server/service/database/oracle.service.ts +145 -105
  60. package/dist/server/service/database/sqlite.service.js +1 -1
  61. package/dist/server/service/database/sqlite.service.js.map +1 -1
  62. package/dist/server/service/database/sqlite.service.ts +1 -1
  63. package/package.json +8 -8
  64. package/scripts/verify-db-support.cjs +239 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fdb2",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "main": "electron/main.js",
@@ -49,6 +49,8 @@
49
49
  "dayjs": "^1.11.21",
50
50
  "electron-updater": "^6.8.9",
51
51
  "express": "^5.2.1",
52
+ "mongodb": "^6.21.0",
53
+ "mssql": "^12.7.13",
52
54
  "mysql2": "^3.22.6",
53
55
  "oracledb": "^6.10.0",
54
56
  "pg": "^8.22.0",
@@ -56,9 +58,6 @@
56
58
  "typeorm": "^0.3.31",
57
59
  "vue-i18n": "^11.4.6"
58
60
  },
59
- "optionalDependencies": {
60
- "better-sqlite3": "^11.10.0"
61
- },
62
61
  "devDependencies": {
63
62
  "@codemirror/commands": "^6.10.4",
64
63
  "@codemirror/fold": "^0.19.4",
@@ -79,10 +78,12 @@
79
78
  "@types/vue": "^2.0.0",
80
79
  "@vitejs/plugin-vue": "^6.0.7",
81
80
  "@vitejs/plugin-vue-jsx": "^5.1.6",
81
+ "@vscode/vsce": "^4.0.0",
82
82
  "@vue/tsconfig": "^0.8.1",
83
83
  "@vueuse/core": "^13.9.0",
84
84
  "@zumer/snapdom": "^1.9.14",
85
85
  "autoprefixer": "^10.5.2",
86
+ "better-sqlite3": "^12.11.1",
86
87
  "bootstrap": "^5.3.8",
87
88
  "bootstrap-icons": "^1.13.1",
88
89
  "codemirror": "^6.0.2",
@@ -91,10 +92,12 @@
91
92
  "electron": "^35.7.5",
92
93
  "electron-builder": "^26.15.3",
93
94
  "electron-updater": "^6.8.3",
95
+ "esbuild": "^0.23.1",
94
96
  "exceljs": "^4.4.0",
95
97
  "js-cookie": "^3.0.8",
96
98
  "jsdom": "^26.1.0",
97
99
  "jszip": "^3.10.1",
100
+ "ovsx": "^0.10.0",
98
101
  "pinia": "^3.0.4",
99
102
  "pinia-plugin-persistedstate": "^4.7.1",
100
103
  "postcss": "^8.5.19",
@@ -109,10 +112,7 @@
109
112
  "vue-router": "^4.6.4",
110
113
  "vue-tsc": "^3.3.7",
111
114
  "vuex": "^4.1.0",
112
- "xlsx": "^0.18.5",
113
- "esbuild": "^0.23.1",
114
- "@vscode/vsce": "^4.0.0",
115
- "ovsx": "^0.10.0"
115
+ "xlsx": "^0.18.5"
116
116
  },
117
117
  "bin": {
118
118
  "fdb2": "bin/fdb2.js"
@@ -0,0 +1,239 @@
1
+ /*
2
+ * 数据库支持端到端验证脚本
3
+ * 直接复用 dist/server 编译产物里的 ConnectionService + DatabaseService(即应用真实代码路径),
4
+ * 对每个数据库类型跑完整链路(调用方式与 server/index.ts 路由一致):
5
+ * 建连 -> getDatabases -> getTables -> 建表 -> getColumns -> 插入 -> 查数据
6
+ * -> 改表/校验 -> 导出schema -> 删表
7
+ *
8
+ * 关系型(postgres/cockroachdb/oracle/mssql/sqlite)走 DDL spec 建表/改表 + SQL 查询;
9
+ * MongoDB 走服务自身的集合方法(文档库无 SQL DDL)。
10
+ *
11
+ * 用法:
12
+ * DB_TOOL_DATA_DIR=/tmp/dbtool-verify node scripts/verify-db-support.cjs [type ...]
13
+ * type ∈ postgres cockroachdb oracle mssql mongodb sqlite
14
+ */
15
+ 'use strict';
16
+
17
+ const path = require('path');
18
+ const fs = require('fs');
19
+ const os = require('os');
20
+
21
+ const dataDir = process.env.DB_TOOL_DATA_DIR || path.join(os.tmpdir(), 'dbtool-verify-' + Date.now());
22
+ process.env.DB_TOOL_DATA_DIR = dataDir;
23
+ fs.mkdirSync(dataDir, { recursive: true });
24
+
25
+ const { ConnectionService } = require(path.join(__dirname, '..', 'dist', 'server', 'service', 'connection.service.js'));
26
+ const { DatabaseService } = require(path.join(__dirname, '..', 'dist', 'server', 'service', 'database', 'database.service.js'));
27
+
28
+ const CONN = {
29
+ postgres: { type: 'postgres', host: '127.0.0.1', port: 15432, username: 'postgres', password: 'postgres', database: 'testdb', options: {} },
30
+ cockroachdb: { type: 'cockroachdb', host: '127.0.0.1', port: 26257, username: 'root', password: '', database: 'defaultdb', options: {} },
31
+ oracle: { type: 'oracle', host: '127.0.0.1', port: 11521, username: 'system', password: 'oracle', database: 'freepdb1', options: {} },
32
+ mssql: { type: 'mssql', host: '127.0.0.1', port: 11433, username: 'sa', password: 'YourStrong!Passw0rd', database: 'master', options: {} },
33
+ mongodb: { type: 'mongodb', host: '127.0.0.1', port: 27017, username: '', password: '', database: 'testdb', options: {} },
34
+ clickhouse: { type: 'clickhouse', host: '127.0.0.1', port: 18123, username: 'default', password: '', database: 'default', options: {} },
35
+ sqlite: { type: 'sqlite', host: '', port: null, username: '', password: '', database: path.join(dataDir, 'verify.db'), options: {} }
36
+ };
37
+
38
+ const TABLE = 'verify_tbl';
39
+
40
+ function specBase() {
41
+ return {
42
+ name: TABLE, comment: 'verify table',
43
+ columns: [
44
+ { name: 'id', type: 'INT', nullable: false, hasDefault: false, autoIncrement: true },
45
+ { name: 'name', type: 'VARCHAR', length: 100, nullable: true, hasDefault: false },
46
+ { name: 'age', type: 'INT', nullable: true, hasDefault: false }
47
+ ],
48
+ primaryKey: ['id']
49
+ };
50
+ }
51
+ function specAltered() {
52
+ const s = specBase();
53
+ s.columns.push({ name: 'email', type: 'VARCHAR', length: 200, nullable: true, hasDefault: false });
54
+ return s;
55
+ }
56
+
57
+ function step(name, fn) {
58
+ return Promise.resolve()
59
+ .then(fn)
60
+ .then(r => { console.log(` [PASS] ${name}`); return r; })
61
+ .catch(e => {
62
+ const msg = e && e.message ? e.message.split('\n')[0] : String(e);
63
+ console.log(` [FAIL] ${name} -> ${msg}`);
64
+ return { __error: msg };
65
+ });
66
+ }
67
+
68
+ async function runType(type) {
69
+ const cfg = CONN[type];
70
+ if (!cfg) { console.log(`未知类型: ${type}`); return; }
71
+ const isMongo = type === 'mongodb';
72
+ console.log(`\n==================== ${type} ====================`);
73
+ const connService = new ConnectionService();
74
+ await connService.init();
75
+ const dispatcher = new DatabaseService();
76
+ dispatcher.connectionService = connService;
77
+
78
+ const baseOf = async (database) => {
79
+ const ds = await connService.getActiveConnection(id, database);
80
+ return { ds, base: dispatcher['getDatabaseService'](ds.options.type) };
81
+ };
82
+
83
+ const conn = await connService.addConnection({ ...cfg, name: 'verify_' + type, enabled: true });
84
+ const id = conn.id;
85
+ const db = cfg.database;
86
+
87
+ await step('testConnection', async () => {
88
+ const ok = await connService.testConnection(conn);
89
+ if (!ok) throw new Error('连接测试失败');
90
+ });
91
+
92
+ await step('getDatabases', async () => {
93
+ const list = await dispatcher.getDatabases(id);
94
+ if (!Array.isArray(list)) throw new Error('返回非数组');
95
+ return list;
96
+ });
97
+
98
+ await step('getTables(初始)', async () => {
99
+ const list = await dispatcher.getTables(id, db);
100
+ if (!Array.isArray(list)) throw new Error('返回非数组');
101
+ return list;
102
+ });
103
+
104
+ // 清理残留
105
+ await (async () => {
106
+ try { const { ds, base } = await baseOf(db); await base.dropTable(ds, db, TABLE); } catch (e) { /* ignore */ }
107
+ })();
108
+
109
+ if (isMongo) {
110
+ await step('createTable(集合)', async () => {
111
+ const { ds, base } = await baseOf(db);
112
+ await base.createTable(ds, db, specBase());
113
+ });
114
+ await step('getTables(建表后含 verify_tbl)', async () => {
115
+ const list = await dispatcher.getTables(id, db);
116
+ const found = list.some(t => String(t.name).toLowerCase() === TABLE.toLowerCase());
117
+ if (!found) throw new Error('建表后未找到 ' + TABLE);
118
+ return list;
119
+ });
120
+ await step('insertData', async () => {
121
+ const { ds, base } = await baseOf(db);
122
+ await base.insertData(ds, db, TABLE, { name: 'alice', age: 30 });
123
+ await base.insertData(ds, db, TABLE, { name: 'bob', age: 25 });
124
+ });
125
+ await step('getTableData(原生find)', async () => {
126
+ const { ds } = await baseOf(db);
127
+ const docs = await ds.driver.queryRunner.databaseConnection.db(db).collection(TABLE).find().toArray();
128
+ if (!Array.isArray(docs) || docs.length < 2) throw new Error('数据行不足');
129
+ return docs;
130
+ });
131
+ await step('getColumns(插入后)', async () => {
132
+ const { ds, base } = await baseOf(db);
133
+ const cols = await base.getColumns(ds, db, TABLE);
134
+ if (!Array.isArray(cols) || cols.length < 3) throw new Error('列数不足: ' + (cols && cols.length));
135
+ return cols.map(c => c.name + ':' + c.type);
136
+ });
137
+ await step('exportSchema', async () => {
138
+ const s = await dispatcher.exportSchema(id, db);
139
+ if (!s || !s.trim()) throw new Error('schema 为空');
140
+ return s;
141
+ });
142
+ await step('dropTable(清理)', async () => {
143
+ const { ds, base } = await baseOf(db);
144
+ await base.dropTable(ds, db, TABLE);
145
+ });
146
+ } else {
147
+ await step('previewCreateTable', async () => {
148
+ const p = await dispatcher.previewCreateTable(id, db, specBase());
149
+ if (!p.preview || !p.preview.trim()) throw new Error('预览为空');
150
+ return p.preview;
151
+ });
152
+
153
+ await step('executeCreateTable', async () => {
154
+ const r = await dispatcher.executeCreateTable(id, db, specBase());
155
+ if (!r.statements || r.statements.length === 0) throw new Error('未生成建表语句');
156
+ return r.statements;
157
+ });
158
+
159
+ await step('getTables(建表后含 verify_tbl)', async () => {
160
+ const list = await dispatcher.getTables(id, db);
161
+ const found = list.some(t => String(t.name).toLowerCase() === TABLE.toLowerCase());
162
+ if (!found) throw new Error('建表后未找到 ' + TABLE);
163
+ return list;
164
+ });
165
+
166
+ await step('getColumns', async () => {
167
+ const { ds, base } = await baseOf(db);
168
+ const cols = await base.getColumns(ds, db, TABLE);
169
+ if (!Array.isArray(cols) || cols.length < 3) throw new Error('列数不足: ' + (cols && cols.length));
170
+ return cols.map(c => c.name + ':' + c.type);
171
+ });
172
+
173
+ await step('insertData', async () => {
174
+ const { ds, base } = await baseOf(db);
175
+ const cols = await base.getColumns(ds, db, TABLE);
176
+ const ins = (data) => {
177
+ const names = cols.filter(c => !c.isAutoIncrement).map(c => base['quoteIdentifier'](c.name));
178
+ const vals = cols.filter(c => !c.isAutoIncrement).map(c => data[c.name] === undefined ? 'NULL' : (typeof data[c.name] === 'string' ? `'${String(data[c.name]).replace(/'/g, "''")}'` : String(data[c.name])));
179
+ return `INSERT INTO ${base['quoteIdentifier'](TABLE)} (${names.join(',')}) VALUES (${vals.join(',')})`;
180
+ };
181
+ await dispatcher.executeQuery(id, ins({ name: 'alice', age: 30 }), db);
182
+ await dispatcher.executeQuery(id, ins({ name: 'bob', age: 25 }), db);
183
+ });
184
+
185
+ await step('getTableData', async () => {
186
+ const r = await dispatcher.getTableData(id, db, TABLE, 1, 100);
187
+ if (!r || !Array.isArray(r.data) || r.data.length < 2) throw new Error('数据行不足');
188
+ return r.data;
189
+ });
190
+
191
+ await step('executeQuery(SELECT COUNT)', async () => {
192
+ const { base } = await baseOf(db);
193
+ const r = await dispatcher.executeQuery(id, `SELECT COUNT(*) AS c FROM ${base['quoteIdentifier'](TABLE)}`, db);
194
+ return r;
195
+ });
196
+
197
+ await step('previewAlterTable(加 email 列)', async () => {
198
+ const p = await dispatcher.previewAlterTable(id, db, TABLE, specAltered());
199
+ if (!p.preview || !p.preview.trim()) throw new Error('预览为空');
200
+ return p.preview;
201
+ });
202
+
203
+ await step('executeAlterTable(加 email 列)', async () => {
204
+ const r = await dispatcher.executeAlterTable(id, db, TABLE, specAltered());
205
+ if (!r.statements || r.statements.length === 0) throw new Error('未生成改表语句');
206
+ return r.statements;
207
+ });
208
+
209
+ await step('getColumns(改表后含 email)', async () => {
210
+ const { ds, base } = await baseOf(db);
211
+ const cols = await base.getColumns(ds, db, TABLE);
212
+ const hasEmail = cols.some(c => String(c.name).toLowerCase() === 'email');
213
+ if (!hasEmail) throw new Error('改表后未找到 email 列');
214
+ return cols.map(c => c.name);
215
+ });
216
+
217
+ await step('exportSchema', async () => {
218
+ const s = await dispatcher.exportSchema(id, db);
219
+ if (!s || !s.trim()) throw new Error('schema 为空');
220
+ return s;
221
+ });
222
+
223
+ await step('dropTable(清理)', async () => {
224
+ const { ds, base } = await baseOf(db);
225
+ await base.dropTable(ds, db, TABLE);
226
+ });
227
+ }
228
+
229
+ try { await connService.closeAllConnections(); } catch (e) { /* ignore */ }
230
+ }
231
+
232
+ (async () => {
233
+ const args = process.argv.slice(2);
234
+ const types = args.length ? args : ['postgres', 'cockroachdb', 'oracle', 'mssql', 'mongodb', 'sqlite'];
235
+ console.log('数据目录:', dataDir);
236
+ for (const t of types) { await runType(t); }
237
+ console.log('\n==================== 完成 ====================');
238
+ process.exit(0);
239
+ })().catch(e => { console.error('harness 崩溃:', e); process.exit(1); });