dbgate-api-premium 6.3.3 → 6.4.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/package.json +9 -7
- package/src/controllers/archive.js +99 -6
- package/src/controllers/config.js +135 -22
- package/src/controllers/connections.js +35 -2
- package/src/controllers/databaseConnections.js +76 -1
- package/src/controllers/files.js +59 -0
- package/src/controllers/jsldata.js +9 -0
- package/src/controllers/runners.js +25 -5
- package/src/controllers/serverConnections.js +17 -2
- package/src/controllers/storage.js +51 -1
- package/src/controllers/uploads.js +0 -46
- package/src/currentVersion.js +2 -2
- package/src/proc/connectProcess.js +14 -2
- package/src/proc/databaseConnectionProcess.js +70 -5
- package/src/proc/serverConnectionProcess.js +7 -1
- package/src/proc/sessionProcess.js +15 -178
- package/src/shell/archiveReader.js +3 -1
- package/src/shell/collectorWriter.js +2 -2
- package/src/shell/copyStream.js +1 -0
- package/src/shell/dataReplicator.js +96 -0
- package/src/shell/download.js +22 -6
- package/src/shell/index.js +12 -2
- package/src/shell/jsonLinesWriter.js +4 -3
- package/src/shell/queryReader.js +10 -3
- package/src/shell/unzipDirectory.js +91 -0
- package/src/shell/unzipJsonLinesData.js +60 -0
- package/src/shell/unzipJsonLinesFile.js +59 -0
- package/src/shell/zipDirectory.js +49 -0
- package/src/shell/zipJsonLinesData.js +49 -0
- package/src/utility/cloudUpgrade.js +14 -1
- package/src/utility/crypting.js +56 -5
- package/src/utility/extractSingleFileFromZip.js +77 -0
- package/src/utility/handleQueryStream.js +186 -0
- package/src/utility/listZipEntries.js +41 -0
- package/src/utility/storageReplicatorItems.js +88 -0
- package/src/shell/dataDuplicator.js +0 -61
|
@@ -8,7 +8,7 @@ const { fork, spawn } = require('child_process');
|
|
|
8
8
|
const { rundir, uploadsdir, pluginsdir, getPluginBackendPath, packagedPluginList } = require('../utility/directories');
|
|
9
9
|
const {
|
|
10
10
|
extractShellApiPlugins,
|
|
11
|
-
|
|
11
|
+
compileShellApiFunctionName,
|
|
12
12
|
jsonScriptToJavascript,
|
|
13
13
|
getLogger,
|
|
14
14
|
safeJsonParse,
|
|
@@ -58,7 +58,7 @@ dbgateApi.initializeApiEnvironment();
|
|
|
58
58
|
${requirePluginsTemplate(extractShellApiPlugins(functionName, props))}
|
|
59
59
|
require=null;
|
|
60
60
|
async function run() {
|
|
61
|
-
const reader=await ${
|
|
61
|
+
const reader=await ${compileShellApiFunctionName(functionName)}(${JSON.stringify(props)});
|
|
62
62
|
const writer=await dbgateApi.collectorWriter({runid: '${runid}'});
|
|
63
63
|
await dbgateApi.copyStream(reader, writer);
|
|
64
64
|
}
|
|
@@ -96,9 +96,9 @@ module.exports = {
|
|
|
96
96
|
|
|
97
97
|
handle_ping() {},
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
handle_dataResult(runid, { dataResult }) {
|
|
100
100
|
const { resolve } = this.requests[runid];
|
|
101
|
-
resolve(
|
|
101
|
+
resolve(dataResult);
|
|
102
102
|
delete this.requests[runid];
|
|
103
103
|
},
|
|
104
104
|
|
|
@@ -273,7 +273,7 @@ module.exports = {
|
|
|
273
273
|
const runid = crypto.randomUUID();
|
|
274
274
|
|
|
275
275
|
if (script.type == 'json') {
|
|
276
|
-
const js = jsonScriptToJavascript(script);
|
|
276
|
+
const js = await jsonScriptToJavascript(script);
|
|
277
277
|
return this.startCore(runid, scriptTemplate(js, false));
|
|
278
278
|
}
|
|
279
279
|
|
|
@@ -328,4 +328,24 @@ module.exports = {
|
|
|
328
328
|
});
|
|
329
329
|
return promise;
|
|
330
330
|
},
|
|
331
|
+
|
|
332
|
+
scriptResult_meta: true,
|
|
333
|
+
async scriptResult({ script }) {
|
|
334
|
+
if (script.type != 'json') {
|
|
335
|
+
return { errorMessage: 'Only JSON scripts are allowed' };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const promise = new Promise(async (resolve, reject) => {
|
|
339
|
+
const runid = crypto.randomUUID();
|
|
340
|
+
this.requests[runid] = { resolve, reject, exitOnStreamError: true };
|
|
341
|
+
const cloned = _.cloneDeepWith(script, node => {
|
|
342
|
+
if (node?.$replace == 'runid') {
|
|
343
|
+
return runid;
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
const js = await jsonScriptToJavascript(cloned);
|
|
347
|
+
this.startCore(runid, scriptTemplate(js, false));
|
|
348
|
+
});
|
|
349
|
+
return promise;
|
|
350
|
+
},
|
|
331
351
|
};
|
|
@@ -54,6 +54,9 @@ module.exports = {
|
|
|
54
54
|
if (!connection) {
|
|
55
55
|
throw new Error(`Connection with conid="${conid}" not found`);
|
|
56
56
|
}
|
|
57
|
+
if (connection.singleDatabase) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
57
60
|
if (connection.passwordMode == 'askPassword' || connection.passwordMode == 'askUser') {
|
|
58
61
|
throw new MissingCredentialsError({ conid, passwordMode: connection.passwordMode });
|
|
59
62
|
}
|
|
@@ -142,14 +145,14 @@ module.exports = {
|
|
|
142
145
|
if (conid == '__model') return [];
|
|
143
146
|
testConnectionPermission(conid, req);
|
|
144
147
|
const opened = await this.ensureOpened(conid);
|
|
145
|
-
return opened
|
|
148
|
+
return opened?.databases ?? [];
|
|
146
149
|
},
|
|
147
150
|
|
|
148
151
|
version_meta: true,
|
|
149
152
|
async version({ conid }, req) {
|
|
150
153
|
testConnectionPermission(conid, req);
|
|
151
154
|
const opened = await this.ensureOpened(conid);
|
|
152
|
-
return opened
|
|
155
|
+
return opened?.version ?? null;
|
|
153
156
|
},
|
|
154
157
|
|
|
155
158
|
serverStatus_meta: true,
|
|
@@ -170,6 +173,9 @@ module.exports = {
|
|
|
170
173
|
}
|
|
171
174
|
this.lastPinged[conid] = new Date().getTime();
|
|
172
175
|
const opened = await this.ensureOpened(conid);
|
|
176
|
+
if (!opened) {
|
|
177
|
+
return Promise.resolve();
|
|
178
|
+
}
|
|
173
179
|
try {
|
|
174
180
|
opened.subprocess.send({ msgtype: 'ping' });
|
|
175
181
|
} catch (err) {
|
|
@@ -194,6 +200,9 @@ module.exports = {
|
|
|
194
200
|
async sendDatabaseOp({ conid, msgtype, name }, req) {
|
|
195
201
|
testConnectionPermission(conid, req);
|
|
196
202
|
const opened = await this.ensureOpened(conid);
|
|
203
|
+
if (!opened) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
197
206
|
if (opened.connection.isReadOnly) return false;
|
|
198
207
|
const res = await this.sendRequest(opened, { msgtype, name });
|
|
199
208
|
if (res.errorMessage) {
|
|
@@ -233,6 +242,9 @@ module.exports = {
|
|
|
233
242
|
async loadDataCore(msgtype, { conid, ...args }, req) {
|
|
234
243
|
testConnectionPermission(conid, req);
|
|
235
244
|
const opened = await this.ensureOpened(conid);
|
|
245
|
+
if (!opened) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
236
248
|
const res = await this.sendRequest(opened, { msgtype, ...args });
|
|
237
249
|
if (res.errorMessage) {
|
|
238
250
|
console.error(res.errorMessage);
|
|
@@ -254,6 +266,9 @@ module.exports = {
|
|
|
254
266
|
async summaryCommand({ conid, command, row }, req) {
|
|
255
267
|
testConnectionPermission(conid, req);
|
|
256
268
|
const opened = await this.ensureOpened(conid);
|
|
269
|
+
if (!opened) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
257
272
|
if (opened.connection.isReadOnly) return false;
|
|
258
273
|
return this.loadDataCore('summaryCommand', { conid, command, row });
|
|
259
274
|
},
|
|
@@ -36,6 +36,8 @@ const {
|
|
|
36
36
|
encryptPasswordString,
|
|
37
37
|
} = require('../utility/crypting');
|
|
38
38
|
const crypto = require('crypto');
|
|
39
|
+
const dataReplicator = require('../shell/dataReplicator');
|
|
40
|
+
const storageReplicatorItems = require('../utility/storageReplicatorItems');
|
|
39
41
|
|
|
40
42
|
const logger = getLogger('storage');
|
|
41
43
|
|
|
@@ -597,6 +599,8 @@ module.exports = {
|
|
|
597
599
|
);
|
|
598
600
|
}
|
|
599
601
|
|
|
602
|
+
socket.emitChanged('connection-list-changed');
|
|
603
|
+
|
|
600
604
|
return true;
|
|
601
605
|
},
|
|
602
606
|
|
|
@@ -609,6 +613,8 @@ module.exports = {
|
|
|
609
613
|
|
|
610
614
|
await runQueryFmt(driver, conn, 'delete from ~connections where ~id = %v', id);
|
|
611
615
|
|
|
616
|
+
socket.emitChanged('connection-list-changed');
|
|
617
|
+
|
|
612
618
|
return true;
|
|
613
619
|
},
|
|
614
620
|
|
|
@@ -627,7 +633,7 @@ module.exports = {
|
|
|
627
633
|
const resp =
|
|
628
634
|
id == 'new' ? {} : await storageSelectFmt(`select ~roles.~id,~roles.~name from ~roles where ~roles.~id = %v`, id);
|
|
629
635
|
|
|
630
|
-
const basePermissions = getPredefinedPermissions(resp?.name
|
|
636
|
+
const basePermissions = getPredefinedPermissions(id < 0 && resp[0]?.name ? resp[0]?.name : 'logged-user');
|
|
631
637
|
const permissions =
|
|
632
638
|
id == 'new'
|
|
633
639
|
? []
|
|
@@ -692,4 +698,48 @@ module.exports = {
|
|
|
692
698
|
|
|
693
699
|
return true;
|
|
694
700
|
},
|
|
701
|
+
|
|
702
|
+
async getExportedDatabase() {
|
|
703
|
+
const connections = await storageSelectFmt(`select * from ~connections`);
|
|
704
|
+
const user_permissions = await storageSelectFmt(`select * from ~user_permissions`);
|
|
705
|
+
const users = await storageSelectFmt(`select * from ~users`);
|
|
706
|
+
const auth_methods = await storageSelectFmt(`select * from ~auth_methods`);
|
|
707
|
+
const role_connections = await storageSelectFmt(`select * from ~role_connections`);
|
|
708
|
+
const user_connections = await storageSelectFmt(`select * from ~user_connections`);
|
|
709
|
+
const user_roles = await storageSelectFmt(`select * from ~user_roles`);
|
|
710
|
+
const config = await storageSelectFmt(`select * from ~config`);
|
|
711
|
+
const auth_methods_config = await storageSelectFmt(`select * from ~auth_methods_config`);
|
|
712
|
+
const role_permissions = await storageSelectFmt(`select * from ~role_permissions`);
|
|
713
|
+
const roles = await storageSelectFmt(`select * from ~roles`);
|
|
714
|
+
|
|
715
|
+
return {
|
|
716
|
+
connections,
|
|
717
|
+
user_permissions,
|
|
718
|
+
users,
|
|
719
|
+
auth_methods,
|
|
720
|
+
role_connections,
|
|
721
|
+
user_connections,
|
|
722
|
+
user_roles,
|
|
723
|
+
config,
|
|
724
|
+
auth_methods_config,
|
|
725
|
+
role_permissions,
|
|
726
|
+
roles,
|
|
727
|
+
};
|
|
728
|
+
},
|
|
729
|
+
|
|
730
|
+
async replicateImportedDatabase(db) {
|
|
731
|
+
const [conn, driver] = await getStorageConnection();
|
|
732
|
+
// @ts-ignore
|
|
733
|
+
await dataReplicator({
|
|
734
|
+
systemConnection: conn,
|
|
735
|
+
driver,
|
|
736
|
+
items: storageReplicatorItems
|
|
737
|
+
.map(item => ({
|
|
738
|
+
...item,
|
|
739
|
+
jsonArray: db[item.name],
|
|
740
|
+
}))
|
|
741
|
+
.filter(x => x.jsonArray),
|
|
742
|
+
});
|
|
743
|
+
socket.emitChanged('connection-list-changed');
|
|
744
|
+
},
|
|
695
745
|
};
|
|
@@ -39,52 +39,6 @@ module.exports = {
|
|
|
39
39
|
});
|
|
40
40
|
},
|
|
41
41
|
|
|
42
|
-
uploadDataFile_meta: {
|
|
43
|
-
method: 'post',
|
|
44
|
-
raw: true,
|
|
45
|
-
},
|
|
46
|
-
uploadDataFile(req, res) {
|
|
47
|
-
const { data } = req.files || {};
|
|
48
|
-
|
|
49
|
-
if (!data) {
|
|
50
|
-
res.json(null);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (data.name.toLowerCase().endsWith('.sql')) {
|
|
55
|
-
logger.info(`Uploading SQL file ${data.name}, size=${data.size}`);
|
|
56
|
-
data.mv(path.join(filesdir(), 'sql', data.name), () => {
|
|
57
|
-
res.json({
|
|
58
|
-
name: data.name,
|
|
59
|
-
folder: 'sql',
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
socket.emitChanged(`files-changed`, { folder: 'sql' });
|
|
63
|
-
socket.emitChanged(`all-files-changed`);
|
|
64
|
-
});
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
res.json(null);
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
saveDataFile_meta: true,
|
|
72
|
-
async saveDataFile({ filePath }) {
|
|
73
|
-
if (filePath.toLowerCase().endsWith('.sql')) {
|
|
74
|
-
logger.info(`Saving SQL file ${filePath}`);
|
|
75
|
-
await fs.copyFile(filePath, path.join(filesdir(), 'sql', path.basename(filePath)));
|
|
76
|
-
|
|
77
|
-
socket.emitChanged(`files-changed`, { folder: 'sql' });
|
|
78
|
-
socket.emitChanged(`all-files-changed`);
|
|
79
|
-
return {
|
|
80
|
-
name: path.basename(filePath),
|
|
81
|
-
folder: 'sql',
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return null;
|
|
86
|
-
},
|
|
87
|
-
|
|
88
42
|
get_meta: {
|
|
89
43
|
method: 'get',
|
|
90
44
|
raw: true,
|
package/src/currentVersion.js
CHANGED
|
@@ -4,6 +4,8 @@ const { connectUtility } = require('../utility/connectUtility');
|
|
|
4
4
|
const { handleProcessCommunication } = require('../utility/processComm');
|
|
5
5
|
const { pickSafeConnectionInfo } = require('../utility/crypting');
|
|
6
6
|
const _ = require('lodash');
|
|
7
|
+
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
|
8
|
+
const logger = getLogger('connectProcess');
|
|
7
9
|
|
|
8
10
|
const formatErrorDetail = (e, connection) => `${e.stack}
|
|
9
11
|
|
|
@@ -23,12 +25,22 @@ function start() {
|
|
|
23
25
|
try {
|
|
24
26
|
const driver = requireEngineDriver(connection);
|
|
25
27
|
const dbhan = await connectUtility(driver, connection, 'app');
|
|
26
|
-
|
|
28
|
+
let version = {
|
|
29
|
+
version: 'Unknown',
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
version = await driver.getVersion(dbhan);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
logger.error(extractErrorLogData(err), 'Error getting DB server version');
|
|
35
|
+
version = {
|
|
36
|
+
version: 'Unknown',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
27
39
|
let databases = undefined;
|
|
28
40
|
if (requestDbList) {
|
|
29
41
|
databases = await driver.listDatabases(dbhan);
|
|
30
42
|
}
|
|
31
|
-
process.send({ msgtype: 'connected', ...
|
|
43
|
+
process.send({ msgtype: 'connected', ...version, databases });
|
|
32
44
|
await driver.close(dbhan);
|
|
33
45
|
} catch (e) {
|
|
34
46
|
console.error(e);
|
|
@@ -9,13 +9,21 @@ const {
|
|
|
9
9
|
dbNameLogCategory,
|
|
10
10
|
extractErrorMessage,
|
|
11
11
|
extractErrorLogData,
|
|
12
|
+
ScriptWriterEval,
|
|
13
|
+
SqlGenerator,
|
|
14
|
+
playJsonScriptWriter,
|
|
12
15
|
} = require('dbgate-tools');
|
|
13
16
|
const requireEngineDriver = require('../utility/requireEngineDriver');
|
|
14
17
|
const { connectUtility } = require('../utility/connectUtility');
|
|
15
18
|
const { handleProcessCommunication } = require('../utility/processComm');
|
|
16
|
-
const { SqlGenerator } = require('dbgate-tools');
|
|
17
19
|
const generateDeploySql = require('../shell/generateDeploySql');
|
|
18
20
|
const { dumpSqlSelect } = require('dbgate-sqltree');
|
|
21
|
+
const { allowExecuteCustomScript, handleQueryStream } = require('../utility/handleQueryStream');
|
|
22
|
+
const dbgateApi = require('../shell');
|
|
23
|
+
const requirePlugin = require('../shell/requirePlugin');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const { rundir } = require('../utility/directories');
|
|
26
|
+
const fs = require('fs-extra');
|
|
19
27
|
|
|
20
28
|
const logger = getLogger('dbconnProcess');
|
|
21
29
|
|
|
@@ -120,10 +128,15 @@ function setStatusName(name) {
|
|
|
120
128
|
|
|
121
129
|
async function readVersion() {
|
|
122
130
|
const driver = requireEngineDriver(storedConnection);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
131
|
+
try {
|
|
132
|
+
const version = await driver.getVersion(dbhan);
|
|
133
|
+
logger.debug(`Got server version: ${version.version}`);
|
|
134
|
+
serverVersion = version;
|
|
135
|
+
} catch (err) {
|
|
136
|
+
logger.error(extractErrorLogData(err), 'Error getting DB server version');
|
|
137
|
+
serverVersion = { version: 'Unknown' };
|
|
138
|
+
}
|
|
139
|
+
process.send({ msgtype: 'version', version: serverVersion });
|
|
127
140
|
}
|
|
128
141
|
|
|
129
142
|
async function handleConnect({ connection, structure, globalSettings }) {
|
|
@@ -370,6 +383,56 @@ async function handleGenerateDeploySql({ msgid, modelFolder }) {
|
|
|
370
383
|
}
|
|
371
384
|
}
|
|
372
385
|
|
|
386
|
+
async function handleExecuteSessionQuery({ sesid, sql }) {
|
|
387
|
+
await waitConnected();
|
|
388
|
+
const driver = requireEngineDriver(storedConnection);
|
|
389
|
+
|
|
390
|
+
if (!allowExecuteCustomScript(storedConnection, driver)) {
|
|
391
|
+
process.send({
|
|
392
|
+
msgtype: 'info',
|
|
393
|
+
info: {
|
|
394
|
+
message: 'Connection without read-only sessions is read only',
|
|
395
|
+
severity: 'error',
|
|
396
|
+
},
|
|
397
|
+
sesid,
|
|
398
|
+
});
|
|
399
|
+
process.send({ msgtype: 'done', sesid, skipFinishedMessage: true });
|
|
400
|
+
return;
|
|
401
|
+
//process.send({ msgtype: 'error', error: e.message });
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const queryStreamInfoHolder = {
|
|
405
|
+
resultIndex: 0,
|
|
406
|
+
canceled: false,
|
|
407
|
+
};
|
|
408
|
+
for (const sqlItem of splitQuery(sql, {
|
|
409
|
+
...driver.getQuerySplitterOptions('stream'),
|
|
410
|
+
returnRichInfo: true,
|
|
411
|
+
})) {
|
|
412
|
+
await handleQueryStream(dbhan, driver, queryStreamInfoHolder, sqlItem, sesid);
|
|
413
|
+
if (queryStreamInfoHolder.canceled) {
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
process.send({ msgtype: 'done', sesid });
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async function handleEvalJsonScript({ script, runid }) {
|
|
421
|
+
const directory = path.join(rundir(), runid);
|
|
422
|
+
fs.mkdirSync(directory);
|
|
423
|
+
const originalCwd = process.cwd();
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
process.chdir(directory);
|
|
427
|
+
|
|
428
|
+
const evalWriter = new ScriptWriterEval(dbgateApi, requirePlugin, dbhan, runid);
|
|
429
|
+
await playJsonScriptWriter(script, evalWriter);
|
|
430
|
+
process.send({ msgtype: 'runnerDone', runid });
|
|
431
|
+
} finally {
|
|
432
|
+
process.chdir(originalCwd);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
373
436
|
// async function handleRunCommand({ msgid, sql }) {
|
|
374
437
|
// await waitConnected();
|
|
375
438
|
// const driver = engines(storedConnection);
|
|
@@ -400,6 +463,8 @@ const messageHandlers = {
|
|
|
400
463
|
sqlSelect: handleSqlSelect,
|
|
401
464
|
exportKeys: handleExportKeys,
|
|
402
465
|
schemaList: handleSchemaList,
|
|
466
|
+
executeSessionQuery: handleExecuteSessionQuery,
|
|
467
|
+
evalJsonScript: handleEvalJsonScript,
|
|
403
468
|
// runCommand: handleRunCommand,
|
|
404
469
|
};
|
|
405
470
|
|
|
@@ -46,7 +46,13 @@ async function handleRefresh() {
|
|
|
46
46
|
|
|
47
47
|
async function readVersion() {
|
|
48
48
|
const driver = requireEngineDriver(storedConnection);
|
|
49
|
-
|
|
49
|
+
let version;
|
|
50
|
+
try {
|
|
51
|
+
version = await driver.getVersion(dbhan);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
logger.error(extractErrorLogData(err), 'Error getting DB server version');
|
|
54
|
+
version = { version: 'Unknown' };
|
|
55
|
+
}
|
|
50
56
|
process.send({ msgtype: 'version', version });
|
|
51
57
|
}
|
|
52
58
|
|
|
@@ -11,6 +11,7 @@ const { decryptConnection } = require('../utility/crypting');
|
|
|
11
11
|
const { connectUtility } = require('../utility/connectUtility');
|
|
12
12
|
const { handleProcessCommunication } = require('../utility/processComm');
|
|
13
13
|
const { getLogger, extractIntSettingsValue, extractBoolSettingsValue } = require('dbgate-tools');
|
|
14
|
+
const { handleQueryStream, QueryStreamTableWriter, allowExecuteCustomScript } = require('../utility/handleQueryStream');
|
|
14
15
|
|
|
15
16
|
const logger = getLogger('sessionProcess');
|
|
16
17
|
|
|
@@ -23,175 +24,6 @@ let lastActivity = null;
|
|
|
23
24
|
let currentProfiler = null;
|
|
24
25
|
let executingScripts = 0;
|
|
25
26
|
|
|
26
|
-
class TableWriter {
|
|
27
|
-
constructor() {
|
|
28
|
-
this.currentRowCount = 0;
|
|
29
|
-
this.currentChangeIndex = 1;
|
|
30
|
-
this.initializedFile = false;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
initializeFromQuery(structure, resultIndex) {
|
|
34
|
-
this.jslid = crypto.randomUUID();
|
|
35
|
-
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
|
36
|
-
fs.writeFileSync(
|
|
37
|
-
this.currentFile,
|
|
38
|
-
JSON.stringify({
|
|
39
|
-
...structure,
|
|
40
|
-
__isStreamHeader: true,
|
|
41
|
-
}) + '\n'
|
|
42
|
-
);
|
|
43
|
-
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
|
44
|
-
this.writeCurrentStats(false, false);
|
|
45
|
-
this.resultIndex = resultIndex;
|
|
46
|
-
this.initializedFile = true;
|
|
47
|
-
process.send({ msgtype: 'recordset', jslid: this.jslid, resultIndex });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
initializeFromReader(jslid) {
|
|
51
|
-
this.jslid = jslid;
|
|
52
|
-
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
|
53
|
-
this.writeCurrentStats(false, false);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
row(row) {
|
|
57
|
-
// console.log('ACCEPT ROW', row);
|
|
58
|
-
this.currentStream.write(JSON.stringify(row) + '\n');
|
|
59
|
-
this.currentRowCount += 1;
|
|
60
|
-
|
|
61
|
-
if (!this.plannedStats) {
|
|
62
|
-
this.plannedStats = true;
|
|
63
|
-
process.nextTick(() => {
|
|
64
|
-
if (this.currentStream) this.currentStream.uncork();
|
|
65
|
-
process.nextTick(() => this.writeCurrentStats(false, true));
|
|
66
|
-
this.plannedStats = false;
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
rowFromReader(row) {
|
|
72
|
-
if (!this.initializedFile) {
|
|
73
|
-
process.send({ msgtype: 'initializeFile', jslid: this.jslid });
|
|
74
|
-
this.initializedFile = true;
|
|
75
|
-
|
|
76
|
-
fs.writeFileSync(this.currentFile, JSON.stringify(row) + '\n');
|
|
77
|
-
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
|
78
|
-
this.writeCurrentStats(false, false);
|
|
79
|
-
this.initializedFile = true;
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
this.row(row);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
writeCurrentStats(isFinished = false, emitEvent = false) {
|
|
87
|
-
const stats = {
|
|
88
|
-
rowCount: this.currentRowCount,
|
|
89
|
-
changeIndex: this.currentChangeIndex,
|
|
90
|
-
isFinished,
|
|
91
|
-
jslid: this.jslid,
|
|
92
|
-
};
|
|
93
|
-
fs.writeFileSync(`${this.currentFile}.stats`, JSON.stringify(stats));
|
|
94
|
-
this.currentChangeIndex += 1;
|
|
95
|
-
if (emitEvent) {
|
|
96
|
-
process.send({ msgtype: 'stats', ...stats });
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
close(afterClose) {
|
|
101
|
-
if (this.currentStream) {
|
|
102
|
-
this.currentStream.end(() => {
|
|
103
|
-
this.writeCurrentStats(true, true);
|
|
104
|
-
if (afterClose) afterClose();
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
class StreamHandler {
|
|
111
|
-
constructor(resultIndexHolder, resolve, startLine) {
|
|
112
|
-
this.recordset = this.recordset.bind(this);
|
|
113
|
-
this.startLine = startLine;
|
|
114
|
-
this.row = this.row.bind(this);
|
|
115
|
-
// this.error = this.error.bind(this);
|
|
116
|
-
this.done = this.done.bind(this);
|
|
117
|
-
this.info = this.info.bind(this);
|
|
118
|
-
|
|
119
|
-
// use this for cancelling - not implemented
|
|
120
|
-
// this.stream = null;
|
|
121
|
-
|
|
122
|
-
this.plannedStats = false;
|
|
123
|
-
this.resultIndexHolder = resultIndexHolder;
|
|
124
|
-
this.resolve = resolve;
|
|
125
|
-
// currentHandlers = [...currentHandlers, this];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
closeCurrentWriter() {
|
|
129
|
-
if (this.currentWriter) {
|
|
130
|
-
this.currentWriter.close();
|
|
131
|
-
this.currentWriter = null;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
recordset(columns) {
|
|
136
|
-
this.closeCurrentWriter();
|
|
137
|
-
this.currentWriter = new TableWriter();
|
|
138
|
-
this.currentWriter.initializeFromQuery(
|
|
139
|
-
Array.isArray(columns) ? { columns } : columns,
|
|
140
|
-
this.resultIndexHolder.value
|
|
141
|
-
);
|
|
142
|
-
this.resultIndexHolder.value += 1;
|
|
143
|
-
|
|
144
|
-
// this.writeCurrentStats();
|
|
145
|
-
|
|
146
|
-
// this.onRow = _.throttle((jslid) => {
|
|
147
|
-
// if (jslid == this.jslid) {
|
|
148
|
-
// this.writeCurrentStats(false, true);
|
|
149
|
-
// }
|
|
150
|
-
// }, 500);
|
|
151
|
-
}
|
|
152
|
-
row(row) {
|
|
153
|
-
if (this.currentWriter) this.currentWriter.row(row);
|
|
154
|
-
else if (row.message) process.send({ msgtype: 'info', info: { message: row.message } });
|
|
155
|
-
// this.onRow(this.jslid);
|
|
156
|
-
}
|
|
157
|
-
// error(error) {
|
|
158
|
-
// process.send({ msgtype: 'error', error });
|
|
159
|
-
// }
|
|
160
|
-
done(result) {
|
|
161
|
-
this.closeCurrentWriter();
|
|
162
|
-
// currentHandlers = currentHandlers.filter((x) => x != this);
|
|
163
|
-
this.resolve();
|
|
164
|
-
}
|
|
165
|
-
info(info) {
|
|
166
|
-
if (info && info.line != null) {
|
|
167
|
-
info = {
|
|
168
|
-
...info,
|
|
169
|
-
line: this.startLine + info.line,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
process.send({ msgtype: 'info', info });
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function handleStream(driver, resultIndexHolder, sqlItem) {
|
|
177
|
-
return new Promise((resolve, reject) => {
|
|
178
|
-
const start = sqlItem.trimStart || sqlItem.start;
|
|
179
|
-
const handler = new StreamHandler(resultIndexHolder, resolve, start && start.line);
|
|
180
|
-
driver.stream(dbhan, sqlItem.text, handler);
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function allowExecuteCustomScript(driver) {
|
|
185
|
-
if (driver.readOnlySessions) {
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
if (storedConnection.isReadOnly) {
|
|
189
|
-
return false;
|
|
190
|
-
// throw new Error('Connection is read only');
|
|
191
|
-
}
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
27
|
async function handleConnect(connection) {
|
|
196
28
|
storedConnection = connection;
|
|
197
29
|
|
|
@@ -222,12 +54,12 @@ async function handleStartProfiler({ jslid }) {
|
|
|
222
54
|
await waitConnected();
|
|
223
55
|
const driver = requireEngineDriver(storedConnection);
|
|
224
56
|
|
|
225
|
-
if (!allowExecuteCustomScript(driver)) {
|
|
57
|
+
if (!allowExecuteCustomScript(storedConnection, driver)) {
|
|
226
58
|
process.send({ msgtype: 'done' });
|
|
227
59
|
return;
|
|
228
60
|
}
|
|
229
61
|
|
|
230
|
-
const writer = new
|
|
62
|
+
const writer = new QueryStreamTableWriter();
|
|
231
63
|
writer.initializeFromReader(jslid);
|
|
232
64
|
|
|
233
65
|
currentProfiler = await driver.startProfiler(dbhan, {
|
|
@@ -251,7 +83,7 @@ async function handleExecuteControlCommand({ command }) {
|
|
|
251
83
|
await waitConnected();
|
|
252
84
|
const driver = requireEngineDriver(storedConnection);
|
|
253
85
|
|
|
254
|
-
if (command == 'commitTransaction' && !allowExecuteCustomScript(driver)) {
|
|
86
|
+
if (command == 'commitTransaction' && !allowExecuteCustomScript(storedConnection, driver)) {
|
|
255
87
|
process.send({
|
|
256
88
|
msgtype: 'info',
|
|
257
89
|
info: {
|
|
@@ -291,7 +123,7 @@ async function handleExecuteQuery({ sql, autoCommit }) {
|
|
|
291
123
|
await waitConnected();
|
|
292
124
|
const driver = requireEngineDriver(storedConnection);
|
|
293
125
|
|
|
294
|
-
if (!allowExecuteCustomScript(driver)) {
|
|
126
|
+
if (!allowExecuteCustomScript(storedConnection, driver)) {
|
|
295
127
|
process.send({
|
|
296
128
|
msgtype: 'info',
|
|
297
129
|
info: {
|
|
@@ -306,18 +138,23 @@ async function handleExecuteQuery({ sql, autoCommit }) {
|
|
|
306
138
|
|
|
307
139
|
executingScripts++;
|
|
308
140
|
try {
|
|
309
|
-
const
|
|
310
|
-
|
|
141
|
+
const queryStreamInfoHolder = {
|
|
142
|
+
resultIndex: 0,
|
|
143
|
+
canceled: false,
|
|
311
144
|
};
|
|
312
145
|
for (const sqlItem of splitQuery(sql, {
|
|
313
146
|
...driver.getQuerySplitterOptions('stream'),
|
|
314
147
|
returnRichInfo: true,
|
|
315
148
|
})) {
|
|
316
|
-
await
|
|
149
|
+
await handleQueryStream(dbhan, driver, queryStreamInfoHolder, sqlItem);
|
|
317
150
|
// const handler = new StreamHandler(resultIndex);
|
|
318
151
|
// const stream = await driver.stream(systemConnection, sqlItem, handler);
|
|
319
152
|
// handler.stream = stream;
|
|
320
153
|
// resultIndex = handler.resultIndex;
|
|
154
|
+
|
|
155
|
+
if (queryStreamInfoHolder.canceled) {
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
321
158
|
}
|
|
322
159
|
process.send({ msgtype: 'done', autoCommit });
|
|
323
160
|
} finally {
|
|
@@ -335,13 +172,13 @@ async function handleExecuteReader({ jslid, sql, fileName }) {
|
|
|
335
172
|
if (fileName) {
|
|
336
173
|
sql = fs.readFileSync(fileName, 'utf-8');
|
|
337
174
|
} else {
|
|
338
|
-
if (!allowExecuteCustomScript(driver)) {
|
|
175
|
+
if (!allowExecuteCustomScript(storedConnection, driver)) {
|
|
339
176
|
process.send({ msgtype: 'done' });
|
|
340
177
|
return;
|
|
341
178
|
}
|
|
342
179
|
}
|
|
343
180
|
|
|
344
|
-
const writer = new
|
|
181
|
+
const writer = new QueryStreamTableWriter();
|
|
345
182
|
writer.initializeFromReader(jslid);
|
|
346
183
|
|
|
347
184
|
const reader = await driver.readQuery(dbhan, sql);
|