Skip to content

Commit 15ed52b

Browse files
vagusXxudafeng
andauthored
feat: impl for sqlite in main and renderer process (#17)
* feat: impl for sqlite in main and renderer process * feat: add style --------- Co-authored-by: xudafeng <xudafeng@126.com>
1 parent 8b818bb commit 15ed52b

File tree

13 files changed

+251
-33
lines changed

13 files changed

+251
-33
lines changed

.husky/pre-commit

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env sh
2+
. "$(dirname -- "$0")/_/husky.sh"
3+
4+
npm run reset:db
5+
npx --no-install lint-staged

.lintstagedrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"*.(js|jsx|ts|tsx)": ["eslint --fix"]
3+
}

package.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"git-contributor": "*",
4949
"gulp": "^4.0.2",
5050
"gulp-nodemon": "^2.5.0",
51-
"husky": "*",
51+
"husky": "^8.0.3",
52+
"lint-staged": "^13.2.1",
5253
"mocha": "*",
5354
"monitor.js": "^2.0.1",
5455
"nyc": "^15.1.0",
@@ -63,15 +64,12 @@
6364
"dev:watch": "gulp dev-app --watch",
6465
"test": "nyc --reporter=lcov --reporter=text mocha",
6566
"lint": "eslint . --fix",
67+
"reset:db": "sh ./scripts/clean-db.sh",
6668
"translate": "easy-i18n-cli -c ./i18n.config.js",
6769
"translate:check": "npm run translate -- --check",
6870
"contributor": "git-contributor",
69-
"ss": "python3 -m http.server 8888"
70-
},
71-
"husky": {
72-
"hooks": {
73-
"pre-commit": "npm run lint"
74-
}
71+
"ss": "python3 -m http.server 8888",
72+
"prepare": "husky install"
7573
},
7674
"license": "MIT"
7775
}

scripts/clean-db.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# loop to clean the database file
2+
for FILE in src/local-storage/sqlite/*.db; do
3+
echo > $FILE
4+
git add $FILE
5+
done

src/local-storage/index.ts

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,27 @@
22

33
const url = require('url');
44
const path = require('path');
5+
const { ipcMain } = require('electron');
56
require('@electron/remote/main').initialize();
67

7-
function initSQLCipher() {
8-
const sqlite3 = require('@journeyapps/sqlcipher').verbose();
9-
console.log(sqlite3);
8+
let dbInstance: null | any = null;
9+
function initDB(dbFilePath: string) {
10+
if (!dbInstance) {
11+
const sqlite3 = require('@journeyapps/sqlcipher').verbose();
12+
dbInstance = new sqlite3.Database(dbFilePath);
13+
14+
// dbInstance.serialize(() => {
15+
// // This is the default, but it is good to specify explicitly:
16+
// dbInstance.run('PRAGMA cipher_compatibility = 4');
17+
18+
// dbInstance.run("PRAGMA key = 'mysecret'");
19+
// dbInstance.run('CREATE TABLE lorem (info TEXT)');
20+
// });
21+
}
22+
return dbInstance;
1023
}
1124

12-
module.exports = (app) => {
25+
module.exports = (app: any) => {
1326
const mainUrl = url.format({
1427
pathname: path.join(__dirname, 'renderer', 'main.html'),
1528
protocol: 'file:',
@@ -43,5 +56,41 @@ module.exports = (app) => {
4356
openDevTools: true,
4457
});
4558
window.loadURL(mainUrl);
46-
initSQLCipher();
59+
60+
// browserWin -> renderer -> main 进程执行 sqlite 操作
61+
ipcMain.on('sqlite:operate', (_, data) => {
62+
const { action, operator = 'run', id, sqlArgs } = data as any;
63+
if (action === 'connect') {
64+
dbInstance = initDB('./src/local-storage/sqlite/test-main.db');
65+
}
66+
67+
if (action === 'exec') {
68+
if (dbInstance) {
69+
dbInstance.serialize(() => {
70+
dbInstance[operator](...sqlArgs, (err: Error, row: any) => {
71+
if (err) {
72+
window.webContents.send('sqlite:operate:reply', {
73+
id,
74+
status: 'failed',
75+
cause: err?.message,
76+
});
77+
return;
78+
}
79+
80+
window.webContents.send('sqlite:operate:reply', {
81+
id,
82+
result: row,
83+
status: 'success',
84+
});
85+
});
86+
});
87+
}
88+
}
89+
90+
if (action === 'close') {
91+
if (dbInstance) {
92+
dbInstance.close();
93+
}
94+
}
95+
});
4796
};

src/local-storage/renderer/main.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ body {
3838
}
3939

4040
.content-item .switch fieldset {
41-
min-height: 110px;
41+
min-height: 140px;
4242
}
4343

4444
.content-item button {

src/local-storage/renderer/main.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,19 @@
8585
type="radio"
8686
id="db2-1"
8787
name="SQLite"
88-
value="ElectronNative"
88+
value="ElectronNativeInRenderer"
8989
checked
9090
/>
91-
<label for="db2-1">Electron Native</label>
91+
<label for="db2-1">Exec in Electron Renderer Proccess</label>
9292
</div>
9393
<div>
9494
<input
9595
type="radio"
9696
id="db2-2"
9797
name="SQLite"
98-
value="ElectronIPC"
98+
value="ElectronIPCToMain"
9999
/>
100-
<label for="db2-2">Electron IPC</label>
100+
<label for="db2-2">Exec in Electron Main Proccess</label>
101101
</div>
102102
<div>
103103
<input
@@ -106,7 +106,7 @@
106106
name="SQLite"
107107
value="WASM"
108108
/>
109-
<label for="db2-3">sql.js with wasm</label>
109+
<label for="db2-3">Use sql.js</label>
110110
</div>
111111
<div>
112112
<input
@@ -115,7 +115,7 @@
115115
name="SQLite"
116116
value="WASMInWorker"
117117
/>
118-
<label for="db2-4">sql.js with wasm in worker</label>
118+
<label for="db2-4">Use sql.js in worker</label>
119119
</div>
120120
</fieldset>
121121
</form>

src/local-storage/renderer/main.js

Lines changed: 131 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
'use strict';
22

3+
const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
4+
35
class MainApp {
46
constructor() {
57
this.perfboardElemContainer = document.querySelector('#perf-board');
68
this.SQLiteLogsContainer = document.querySelector('#SQLiteLogs');
79
this.IndexedDBLogsContainer = document.querySelector('#IndexedDBLogs');
810
this.runOptions = {
911
IndexedDB: 'Default',
10-
SQLite: 'ElectronNative',
12+
SQLite: 'ElectronNativeInRenderer',
1113
};
1214
this.logs = {
1315
IndexedDB: [],
@@ -141,14 +143,17 @@ class MainApp {
141143
}
142144

143145
async runSQLiteWASM(type) {
144-
const SQL = await initSqlJs({
146+
const sqlPromise = initSqlJs({
145147
// Required to load the wasm binary asynchronously. Of course, you can host it wherever you want
146148
// You can omit locateFile completely when running in node
147149
locateFile: () => '../../../node_modules/sql.js/dist/sql-wasm.wasm',
148150
});
149151

150-
const db = new SQL.Database();
151-
db.run('DROP TABLE IF EXISTS todo');
152+
const dataPromise = fetch('../../../test.sqlite').then(res => res.arrayBuffer());
153+
const [SQL, buf] = await Promise.all([sqlPromise, dataPromise]);
154+
const db = new SQL.Database(new Uint8Array(buf));
155+
156+
db.run('DROP TABLE IF EXISTS todo;');
152157
db.run('CREATE TABLE todo (id int, description varchar);');
153158

154159
const { count } = this.runnerConfig;
@@ -178,7 +183,7 @@ class MainApp {
178183
worker.postMessage({
179184
id: 'drop',
180185
action: 'exec',
181-
sql: 'DROP TABLE IF EXISTS todo_worker',
186+
sql: 'DROP TABLE IF EXISTS todo_worker;',
182187
});
183188

184189
// create table
@@ -209,25 +214,142 @@ class MainApp {
209214
};
210215

211216
worker.onerror = e => console.log('Worker error: ', e);
217+
218+
const buf = await fetch('../../../test.sqlite')
219+
.then(res => res.arrayBuffer());
220+
212221
worker.postMessage({
213222
id: 'open',
214223
action: 'open',
224+
buffer: buf,
215225
});
216226
}
217227

218-
async runSQLiteElectronNative(type) {
228+
async runSQLiteElectronNativeInRenderer(type) {
219229
const { count } = this.runnerConfig;
220230
const startTime = Date.now();
221231
this.log(type, `start time: ${startTime}, count: ${count}`);
232+
await window._electron_bridge.sqlConnect();
233+
await window._electron_bridge.sqlExec(
234+
'run',
235+
'DROP TABLE IF EXISTS todo_electron_native;',
236+
);
237+
await window._electron_bridge.sqlExec(
238+
'run',
239+
'CREATE TABLE todo_electron_native (id int, description varchar);',
240+
);
241+
222242
for (let i = 0; i < count; i++) {
223-
await window._electron_bridge.addTestData({
224-
index1: `index_${i}`,
225-
field1: new Array(100).fill('测试').join(''),
243+
await window._electron_bridge.sqlExec(
244+
'run',
245+
'INSERT INTO todo_electron_native VALUES (?,?)',
246+
[i, new Array(100).fill('测试').join('')],
247+
);
248+
console.log('Data added successfully');
249+
}
250+
251+
// 轮训等待写入完成
252+
let cnt;
253+
while (cnt !== count) {
254+
const res = await window._electron_bridge.sqlExec(
255+
'get',
256+
'SELECT count(*) as cnt FROM todo_electron_native',
257+
);
258+
if (res.cnt === count) {
259+
break;
260+
} else {
261+
await sleep(50);
262+
}
263+
}
264+
265+
const endTime = Date.now();
266+
this.log(type, `end time: ${endTime}, use ${((endTime - startTime) / 1000).toFixed(2)}s`);
267+
// await window._electron_bridge.sqlClose();
268+
}
269+
270+
async runSQLiteElectronIPCToMain(type) {
271+
const { count } = this.runnerConfig;
272+
const startTime = Date.now();
273+
this.log(type, `start time: ${startTime}, count: ${count}`);
274+
275+
await this.connectIpcRenderAsync('sqlite:operate', 'connect', {
276+
action: 'connect',
277+
});
278+
279+
await this.connectIpcRenderAsync('sqlite:operate', 'drop', {
280+
action: 'exec',
281+
sqlArgs: ['DROP TABLE IF EXISTS todo_electron_ipc;'],
282+
});
283+
284+
await this.connectIpcRenderAsync('sqlite:operate', 'init', {
285+
action: 'exec',
286+
sqlArgs: ['CREATE TABLE todo_electron_ipc (id int, description varchar);'],
287+
});
288+
289+
for (let i = 0; i < count; i++) {
290+
// 无法确保已经写入成功,只能发送消息成功
291+
await this.connectIpcRenderAsync('sqlite:operate', `insert${i}`, {
292+
action: 'exec',
293+
sqlArgs: [
294+
'INSERT INTO todo_electron_ipc VALUES (?,?)',
295+
[i, new Array(100).fill('测试').join('')],
296+
],
226297
});
298+
227299
console.log('Data added successfully');
228300
}
301+
302+
// 轮训等待写入完成
303+
let cnt;
304+
while (cnt !== count) {
305+
const res = await this.connectIpcRenderAsync('sqlite:operate', 'select_total', {
306+
action: 'exec',
307+
operator: 'get',
308+
sqlArgs: [
309+
'SELECT count(*) as cnt FROM todo_electron_ipc',
310+
],
311+
}, { needRes: true });
312+
if (res.cnt === count) {
313+
break;
314+
} else {
315+
await sleep(50);
316+
}
317+
}
318+
229319
const endTime = Date.now();
230320
this.log(type, `end time: ${endTime}, use ${((endTime - startTime) / 1000).toFixed(2)}s`);
321+
322+
// await this.connectIpcRenderAsync('sqlite:operate', 'close', {
323+
// action: 'close',
324+
// });
325+
}
326+
327+
async connectIpcRenderAsync(channel, msgId, args, options = { needRes: false }) {
328+
// 直接发送
329+
if (!options.needRes) {
330+
window._electron_bridge.ipcRenderer.send(channel, {
331+
...args,
332+
id: msgId,
333+
});
334+
return Promise.resolve();
335+
}
336+
337+
return new Promise((resolve, reject) => {
338+
window._electron_bridge.ipcRenderer.on(`${channel}:reply`, (event, { id, result, status, cause }) => {
339+
if (msgId === id) {
340+
if (status === 'success') {
341+
resolve(result);
342+
} else {
343+
reject(new Error(cause));
344+
}
345+
}
346+
});
347+
348+
window._electron_bridge.ipcRenderer.send('sqlite:operate', {
349+
...args,
350+
id: msgId,
351+
});
352+
});
231353
}
232354

233355
log(type, content = '') {

0 commit comments

Comments
 (0)