dbgate-api 4.7.3 → 4.7.4-alpha.3
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/.env +2 -0
- package/env/singledb/.env +2 -0
- package/package.json +5 -5
- package/src/controllers/config.js +1 -0
- package/src/controllers/connections.js +7 -0
- package/src/controllers/databaseConnections.js +69 -2
- package/src/controllers/jsldata.js +14 -10
- package/src/controllers/runners.js +5 -1
- package/src/controllers/sessions.js +28 -2
- package/src/currentVersion.js +2 -2
- package/src/proc/databaseConnectionProcess.js +57 -2
- package/src/proc/sessionProcess.js +91 -6
- package/src/shell/tableReader.js +1 -1
package/.env
CHANGED
package/env/singledb/.env
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dbgate-api",
|
|
3
3
|
"main": "src/index.js",
|
|
4
|
-
"version": "4.7.3",
|
|
4
|
+
"version": "4.7.4-alpha.3",
|
|
5
5
|
"homepage": "https://dbgate.org/",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"compare-versions": "^3.6.0",
|
|
26
26
|
"cors": "^2.8.5",
|
|
27
27
|
"cross-env": "^6.0.3",
|
|
28
|
-
"dbgate-query-splitter": "^4.7.3",
|
|
29
|
-
"dbgate-sqltree": "^4.7.3",
|
|
30
|
-
"dbgate-tools": "^4.7.3",
|
|
28
|
+
"dbgate-query-splitter": "^4.7.4-alpha.3",
|
|
29
|
+
"dbgate-sqltree": "^4.7.4-alpha.3",
|
|
30
|
+
"dbgate-tools": "^4.7.4-alpha.3",
|
|
31
31
|
"diff": "^5.0.0",
|
|
32
32
|
"diff2html": "^3.4.13",
|
|
33
33
|
"eslint": "^6.8.0",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@types/fs-extra": "^9.0.11",
|
|
65
65
|
"@types/lodash": "^4.14.149",
|
|
66
|
-
"dbgate-types": "^4.7.3",
|
|
66
|
+
"dbgate-types": "^4.7.4-alpha.3",
|
|
67
67
|
"env-cmd": "^10.1.0",
|
|
68
68
|
"node-loader": "^1.0.2",
|
|
69
69
|
"nodemon": "^2.0.2",
|
|
@@ -11,6 +11,7 @@ const { pickSafeConnectionInfo } = require('../utility/crypting');
|
|
|
11
11
|
const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
|
|
12
12
|
|
|
13
13
|
const processArgs = require('../utility/processArgs');
|
|
14
|
+
const { safeJsonParse } = require('dbgate-tools');
|
|
14
15
|
|
|
15
16
|
function getNamedArgs() {
|
|
16
17
|
const res = {};
|
|
@@ -55,6 +56,8 @@ function getPortalCollections() {
|
|
|
55
56
|
(process.env[`FILE_${id}`] ? getDatabaseFileLabel(process.env[`FILE_${id}`]) : null),
|
|
56
57
|
singleDatabase: !!process.env[`DATABASE_${id}`] || !!process.env[`FILE_${id}`],
|
|
57
58
|
displayName: process.env[`LABEL_${id}`],
|
|
59
|
+
isReadOnly: process.env[`READONLY_${id}`],
|
|
60
|
+
databases: process.env[`DBCONFIG_${id}`] ? safeJsonParse(process.env[`DBCONFIG_${id}`]) : null,
|
|
58
61
|
|
|
59
62
|
// SSH tunnel
|
|
60
63
|
useSshTunnel: process.env[`USE_SSH_${id}`],
|
|
@@ -199,6 +202,9 @@ module.exports = {
|
|
|
199
202
|
}
|
|
200
203
|
socket.emitChanged('connection-list-changed');
|
|
201
204
|
socket.emitChanged('used-apps-changed');
|
|
205
|
+
if (this._closeAll) {
|
|
206
|
+
this._closeAll(connection._id);
|
|
207
|
+
}
|
|
202
208
|
// for (const db of connection.databases || []) {
|
|
203
209
|
// socket.emitChanged(`db-apps-changed-${connection._id}-${db.name}`);
|
|
204
210
|
// }
|
|
@@ -240,6 +246,7 @@ module.exports = {
|
|
|
240
246
|
|
|
241
247
|
get_meta: true,
|
|
242
248
|
async get({ conid }) {
|
|
249
|
+
if (!conid) return null;
|
|
243
250
|
if (portalConnections) return portalConnections.find(x => x._id == conid) || null;
|
|
244
251
|
const res = await this.datastore.get(conid);
|
|
245
252
|
return res || null;
|
|
@@ -33,6 +33,10 @@ module.exports = {
|
|
|
33
33
|
closed: {},
|
|
34
34
|
requests: {},
|
|
35
35
|
|
|
36
|
+
async _init() {
|
|
37
|
+
connections._closeAll = conid => this.closeAll(conid);
|
|
38
|
+
},
|
|
39
|
+
|
|
36
40
|
handle_structure(conid, database, { structure }) {
|
|
37
41
|
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
|
38
42
|
if (!existing) return;
|
|
@@ -136,6 +140,13 @@ module.exports = {
|
|
|
136
140
|
return res;
|
|
137
141
|
},
|
|
138
142
|
|
|
143
|
+
sqlSelect_meta: true,
|
|
144
|
+
async sqlSelect({ conid, database, select }) {
|
|
145
|
+
const opened = await this.ensureOpened(conid, database);
|
|
146
|
+
const res = await this.sendRequest(opened, { msgtype: 'sqlSelect', select });
|
|
147
|
+
return res;
|
|
148
|
+
},
|
|
149
|
+
|
|
139
150
|
runScript_meta: true,
|
|
140
151
|
async runScript({ conid, database, sql }) {
|
|
141
152
|
console.log(`Processing script, conid=${conid}, database=${database}, sql=${sql}`);
|
|
@@ -148,14 +159,64 @@ module.exports = {
|
|
|
148
159
|
async collectionData({ conid, database, options }) {
|
|
149
160
|
const opened = await this.ensureOpened(conid, database);
|
|
150
161
|
const res = await this.sendRequest(opened, { msgtype: 'collectionData', options });
|
|
151
|
-
return res.result;
|
|
162
|
+
return res.result || null;
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
async loadDataCore(msgtype, { conid, database, ...args }) {
|
|
166
|
+
const opened = await this.ensureOpened(conid, database);
|
|
167
|
+
const res = await this.sendRequest(opened, { msgtype, ...args });
|
|
168
|
+
if (res.errorMessage) {
|
|
169
|
+
console.error(res.errorMessage);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
errorMessage: res.errorMessage,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return res.result || null;
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
loadKeys_meta: true,
|
|
179
|
+
async loadKeys({ conid, database, root }) {
|
|
180
|
+
return this.loadDataCore('loadKeys', { conid, database, root });
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
loadKeyInfo_meta: true,
|
|
184
|
+
async loadKeyInfo({ conid, database, key }) {
|
|
185
|
+
return this.loadDataCore('loadKeyInfo', { conid, database, key });
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
loadKeyTableRange_meta: true,
|
|
189
|
+
async loadKeyTableRange({ conid, database, key, cursor, count }) {
|
|
190
|
+
return this.loadDataCore('loadKeyTableRange', { conid, database, key, cursor, count });
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
loadFieldValues_meta: true,
|
|
194
|
+
async loadFieldValues({ conid, database, schemaName, pureName, field, search }) {
|
|
195
|
+
return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search });
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
callMethod_meta: true,
|
|
199
|
+
async callMethod({ conid, database, method, args }) {
|
|
200
|
+
return this.loadDataCore('callMethod', { conid, database, method, args });
|
|
201
|
+
|
|
202
|
+
// const opened = await this.ensureOpened(conid, database);
|
|
203
|
+
// const res = await this.sendRequest(opened, { msgtype: 'callMethod', method, args });
|
|
204
|
+
// if (res.errorMessage) {
|
|
205
|
+
// console.error(res.errorMessage);
|
|
206
|
+
// }
|
|
207
|
+
// return res.result || null;
|
|
152
208
|
},
|
|
153
209
|
|
|
154
210
|
updateCollection_meta: true,
|
|
155
211
|
async updateCollection({ conid, database, changeSet }) {
|
|
156
212
|
const opened = await this.ensureOpened(conid, database);
|
|
157
213
|
const res = await this.sendRequest(opened, { msgtype: 'updateCollection', changeSet });
|
|
158
|
-
|
|
214
|
+
if (res.errorMessage) {
|
|
215
|
+
return {
|
|
216
|
+
errorMessage: res.errorMessage,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return res.result || null;
|
|
159
220
|
},
|
|
160
221
|
|
|
161
222
|
status_meta: true,
|
|
@@ -228,6 +289,12 @@ module.exports = {
|
|
|
228
289
|
}
|
|
229
290
|
},
|
|
230
291
|
|
|
292
|
+
closeAll(conid, kill = true) {
|
|
293
|
+
for (const existing of this.opened.filter(x => x.conid == conid)) {
|
|
294
|
+
this.close(conid, existing.database, kill);
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
|
|
231
298
|
disconnect_meta: true,
|
|
232
299
|
async disconnect({ conid, database }) {
|
|
233
300
|
await this.close(conid, database, true);
|
|
@@ -111,18 +111,22 @@ module.exports = {
|
|
|
111
111
|
getInfo_meta: true,
|
|
112
112
|
async getInfo({ jslid }) {
|
|
113
113
|
const file = getJslFileName(jslid);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
try {
|
|
115
|
+
const firstLine = await readFirstLine(file);
|
|
116
|
+
if (firstLine) {
|
|
117
|
+
const parsed = JSON.parse(firstLine);
|
|
118
|
+
if (parsed.__isStreamHeader) {
|
|
119
|
+
return parsed;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
__isStreamHeader: true,
|
|
123
|
+
__isDynamicStructure: true,
|
|
124
|
+
};
|
|
119
125
|
}
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
};
|
|
126
|
+
return null;
|
|
127
|
+
} catch (err) {
|
|
128
|
+
return null;
|
|
124
129
|
}
|
|
125
|
-
return null;
|
|
126
130
|
},
|
|
127
131
|
|
|
128
132
|
getRows_meta: true,
|
|
@@ -148,7 +148,11 @@ module.exports = {
|
|
|
148
148
|
},
|
|
149
149
|
|
|
150
150
|
start_meta: true,
|
|
151
|
-
async start({ script }) {
|
|
151
|
+
async start({ script, isGeneratedScript }) {
|
|
152
|
+
if (!isGeneratedScript && process.env.DISABLE_SHELL) {
|
|
153
|
+
return { errorMessage: 'Shell is disabled' };
|
|
154
|
+
}
|
|
155
|
+
|
|
152
156
|
const runid = uuidv1();
|
|
153
157
|
return this.startCore(runid, scriptTemplate(script, false));
|
|
154
158
|
},
|
|
@@ -4,8 +4,10 @@ const connections = require('./connections');
|
|
|
4
4
|
const socket = require('../utility/socket');
|
|
5
5
|
const { fork } = require('child_process');
|
|
6
6
|
const jsldata = require('./jsldata');
|
|
7
|
+
const path = require('path');
|
|
7
8
|
const { handleProcessCommunication } = require('../utility/processComm');
|
|
8
9
|
const processArgs = require('../utility/processArgs');
|
|
10
|
+
const { appdir } = require('../utility/directories');
|
|
9
11
|
|
|
10
12
|
module.exports = {
|
|
11
13
|
/** @type {import('dbgate-types').OpenedSession[]} */
|
|
@@ -46,9 +48,15 @@ module.exports = {
|
|
|
46
48
|
this.dispatchMessage(sesid, info);
|
|
47
49
|
},
|
|
48
50
|
|
|
49
|
-
handle_done(sesid) {
|
|
51
|
+
handle_done(sesid, props) {
|
|
50
52
|
socket.emit(`session-done-${sesid}`);
|
|
51
|
-
|
|
53
|
+
if (!props.skipFinishedMessage) {
|
|
54
|
+
this.dispatchMessage(sesid, 'Query execution finished');
|
|
55
|
+
}
|
|
56
|
+
const session = this.opened.find(x => x.sesid == sesid);
|
|
57
|
+
if (session.killOnDone) {
|
|
58
|
+
this.kill({ sesid });
|
|
59
|
+
}
|
|
52
60
|
},
|
|
53
61
|
|
|
54
62
|
handle_recordset(sesid, props) {
|
|
@@ -60,6 +68,11 @@ module.exports = {
|
|
|
60
68
|
jsldata.notifyChangedStats(stats);
|
|
61
69
|
},
|
|
62
70
|
|
|
71
|
+
handle_initializeFile(sesid, props) {
|
|
72
|
+
const { jslid } = props;
|
|
73
|
+
socket.emit(`session-initialize-file-${jslid}`);
|
|
74
|
+
},
|
|
75
|
+
|
|
63
76
|
handle_ping() {},
|
|
64
77
|
|
|
65
78
|
create_meta: true,
|
|
@@ -105,6 +118,19 @@ module.exports = {
|
|
|
105
118
|
return { state: 'ok' };
|
|
106
119
|
},
|
|
107
120
|
|
|
121
|
+
executeReader_meta: true,
|
|
122
|
+
async executeReader({ conid, database, sql, queryName, appFolder }) {
|
|
123
|
+
const { sesid } = await this.create({ conid, database });
|
|
124
|
+
const session = this.opened.find(x => x.sesid == sesid);
|
|
125
|
+
session.killOnDone = true;
|
|
126
|
+
const jslid = uuidv1();
|
|
127
|
+
const fileName = queryName && appFolder ? path.join(appdir(), appFolder, `${queryName}.query.sql`) : null;
|
|
128
|
+
|
|
129
|
+
session.subprocess.send({ msgtype: 'executeReader', sql, fileName, jslid });
|
|
130
|
+
|
|
131
|
+
return { jslid };
|
|
132
|
+
},
|
|
133
|
+
|
|
108
134
|
// cancel_meta: true,
|
|
109
135
|
// async cancel({ sesid }) {
|
|
110
136
|
// const session = this.opened.find((x) => x.sesid == sesid);
|
package/src/currentVersion.js
CHANGED
|
@@ -7,6 +7,7 @@ const connectUtility = require('../utility/connectUtility');
|
|
|
7
7
|
const { handleProcessCommunication } = require('../utility/processComm');
|
|
8
8
|
const { SqlGenerator } = require('dbgate-tools');
|
|
9
9
|
const generateDeploySql = require('../shell/generateDeploySql');
|
|
10
|
+
const { dumpSqlSelect } = require('dbgate-sqltree');
|
|
10
11
|
|
|
11
12
|
let systemConnection;
|
|
12
13
|
let storedConnection;
|
|
@@ -154,6 +155,7 @@ async function handleRunScript({ msgid, sql }) {
|
|
|
154
155
|
await waitConnected();
|
|
155
156
|
const driver = requireEngineDriver(storedConnection);
|
|
156
157
|
try {
|
|
158
|
+
ensureExecuteCustomScript(driver);
|
|
157
159
|
await driver.script(systemConnection, sql);
|
|
158
160
|
process.send({ msgtype: 'response', msgid });
|
|
159
161
|
} catch (err) {
|
|
@@ -165,6 +167,7 @@ async function handleQueryData({ msgid, sql }) {
|
|
|
165
167
|
await waitConnected();
|
|
166
168
|
const driver = requireEngineDriver(storedConnection);
|
|
167
169
|
try {
|
|
170
|
+
ensureExecuteCustomScript(driver);
|
|
168
171
|
const res = await driver.query(systemConnection, sql);
|
|
169
172
|
process.send({ msgtype: 'response', msgid, ...res });
|
|
170
173
|
} catch (err) {
|
|
@@ -172,21 +175,67 @@ async function handleQueryData({ msgid, sql }) {
|
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
async function
|
|
178
|
+
async function handleSqlSelect({ msgid, select }) {
|
|
179
|
+
const driver = requireEngineDriver(storedConnection);
|
|
180
|
+
const dmp = driver.createDumper();
|
|
181
|
+
dumpSqlSelect(dmp, select);
|
|
182
|
+
return handleQueryData({ msgid, sql: dmp.s });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function handleDriverDataCore(msgid, callMethod) {
|
|
176
186
|
await waitConnected();
|
|
177
187
|
const driver = requireEngineDriver(storedConnection);
|
|
178
188
|
try {
|
|
179
|
-
const result = await driver
|
|
189
|
+
const result = await callMethod(driver);
|
|
180
190
|
process.send({ msgtype: 'response', msgid, result });
|
|
181
191
|
} catch (err) {
|
|
182
192
|
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
|
183
193
|
}
|
|
184
194
|
}
|
|
185
195
|
|
|
196
|
+
async function handleCollectionData({ msgid, options }) {
|
|
197
|
+
return handleDriverDataCore(msgid, driver => driver.readCollection(systemConnection, options));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function handleLoadKeys({ msgid, root }) {
|
|
201
|
+
return handleDriverDataCore(msgid, driver => driver.loadKeys(systemConnection, root));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function handleLoadKeyInfo({ msgid, key }) {
|
|
205
|
+
return handleDriverDataCore(msgid, driver => driver.loadKeyInfo(systemConnection, key));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function handleCallMethod({ msgid, method, args }) {
|
|
209
|
+
return handleDriverDataCore(msgid, driver => {
|
|
210
|
+
ensureExecuteCustomScript(driver);
|
|
211
|
+
return driver.callMethod(systemConnection, method, args);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function handleLoadKeyTableRange({ msgid, key, cursor, count }) {
|
|
216
|
+
return handleDriverDataCore(msgid, driver => driver.loadKeyTableRange(systemConnection, key, cursor, count));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function handleLoadFieldValues({ msgid, schemaName, pureName, field, search }) {
|
|
220
|
+
return handleDriverDataCore(msgid, driver =>
|
|
221
|
+
driver.loadFieldValues(systemConnection, { schemaName, pureName }, field, search)
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function ensureExecuteCustomScript(driver) {
|
|
226
|
+
if (driver.readOnlySessions) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (storedConnection.isReadOnly) {
|
|
230
|
+
throw new Error('Connection is read only');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
186
234
|
async function handleUpdateCollection({ msgid, changeSet }) {
|
|
187
235
|
await waitConnected();
|
|
188
236
|
const driver = requireEngineDriver(storedConnection);
|
|
189
237
|
try {
|
|
238
|
+
ensureExecuteCustomScript(driver);
|
|
190
239
|
const result = await driver.updateCollection(systemConnection, changeSet);
|
|
191
240
|
process.send({ msgtype: 'response', msgid, result });
|
|
192
241
|
} catch (err) {
|
|
@@ -248,10 +297,16 @@ const messageHandlers = {
|
|
|
248
297
|
runScript: handleRunScript,
|
|
249
298
|
updateCollection: handleUpdateCollection,
|
|
250
299
|
collectionData: handleCollectionData,
|
|
300
|
+
loadKeys: handleLoadKeys,
|
|
301
|
+
loadKeyInfo: handleLoadKeyInfo,
|
|
302
|
+
callMethod: handleCallMethod,
|
|
303
|
+
loadKeyTableRange: handleLoadKeyTableRange,
|
|
251
304
|
sqlPreview: handleSqlPreview,
|
|
252
305
|
ping: handlePing,
|
|
253
306
|
syncModel: handleSyncModel,
|
|
254
307
|
generateDeploySql: handleGenerateDeploySql,
|
|
308
|
+
loadFieldValues: handleLoadFieldValues,
|
|
309
|
+
sqlSelect: handleSqlSelect,
|
|
255
310
|
// runCommand: handleRunCommand,
|
|
256
311
|
};
|
|
257
312
|
|
|
@@ -17,11 +17,15 @@ let afterConnectCallbacks = [];
|
|
|
17
17
|
// let currentHandlers = [];
|
|
18
18
|
|
|
19
19
|
class TableWriter {
|
|
20
|
-
constructor(
|
|
21
|
-
this.jslid = uuidv1();
|
|
22
|
-
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
|
20
|
+
constructor() {
|
|
23
21
|
this.currentRowCount = 0;
|
|
24
22
|
this.currentChangeIndex = 1;
|
|
23
|
+
this.initializedFile = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
initializeFromQuery(structure, resultIndex) {
|
|
27
|
+
this.jslid = uuidv1();
|
|
28
|
+
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
|
25
29
|
fs.writeFileSync(
|
|
26
30
|
this.currentFile,
|
|
27
31
|
JSON.stringify({
|
|
@@ -32,13 +36,21 @@ class TableWriter {
|
|
|
32
36
|
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
|
33
37
|
this.writeCurrentStats(false, false);
|
|
34
38
|
this.resultIndex = resultIndex;
|
|
39
|
+
this.initializedFile = true;
|
|
35
40
|
process.send({ msgtype: 'recordset', jslid: this.jslid, resultIndex });
|
|
36
41
|
}
|
|
37
42
|
|
|
43
|
+
initializeFromReader(jslid) {
|
|
44
|
+
this.jslid = jslid;
|
|
45
|
+
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
|
46
|
+
this.writeCurrentStats(false, false);
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
row(row) {
|
|
39
50
|
// console.log('ACCEPT ROW', row);
|
|
40
51
|
this.currentStream.write(JSON.stringify(row) + '\n');
|
|
41
52
|
this.currentRowCount += 1;
|
|
53
|
+
|
|
42
54
|
if (!this.plannedStats) {
|
|
43
55
|
this.plannedStats = true;
|
|
44
56
|
process.nextTick(() => {
|
|
@@ -49,6 +61,21 @@ class TableWriter {
|
|
|
49
61
|
}
|
|
50
62
|
}
|
|
51
63
|
|
|
64
|
+
rowFromReader(row) {
|
|
65
|
+
if (!this.initializedFile) {
|
|
66
|
+
process.send({ msgtype: 'initializeFile', jslid: this.jslid });
|
|
67
|
+
this.initializedFile = true;
|
|
68
|
+
|
|
69
|
+
fs.writeFileSync(this.currentFile, JSON.stringify(row) + '\n');
|
|
70
|
+
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
|
71
|
+
this.writeCurrentStats(false, false);
|
|
72
|
+
this.initializedFile = true;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.row(row);
|
|
77
|
+
}
|
|
78
|
+
|
|
52
79
|
writeCurrentStats(isFinished = false, emitEvent = false) {
|
|
53
80
|
const stats = {
|
|
54
81
|
rowCount: this.currentRowCount,
|
|
@@ -63,10 +90,11 @@ class TableWriter {
|
|
|
63
90
|
}
|
|
64
91
|
}
|
|
65
92
|
|
|
66
|
-
close() {
|
|
93
|
+
close(afterClose) {
|
|
67
94
|
if (this.currentStream) {
|
|
68
95
|
this.currentStream.end(() => {
|
|
69
96
|
this.writeCurrentStats(true, true);
|
|
97
|
+
if (afterClose) afterClose();
|
|
70
98
|
});
|
|
71
99
|
}
|
|
72
100
|
}
|
|
@@ -98,7 +126,11 @@ class StreamHandler {
|
|
|
98
126
|
|
|
99
127
|
recordset(columns) {
|
|
100
128
|
this.closeCurrentWriter();
|
|
101
|
-
this.currentWriter = new TableWriter(
|
|
129
|
+
this.currentWriter = new TableWriter();
|
|
130
|
+
this.currentWriter.initializeFromQuery(
|
|
131
|
+
Array.isArray(columns) ? { columns } : columns,
|
|
132
|
+
this.resultIndexHolder.value
|
|
133
|
+
);
|
|
102
134
|
this.resultIndexHolder.value += 1;
|
|
103
135
|
|
|
104
136
|
// this.writeCurrentStats();
|
|
@@ -110,7 +142,6 @@ class StreamHandler {
|
|
|
110
142
|
// }, 500);
|
|
111
143
|
}
|
|
112
144
|
row(row) {
|
|
113
|
-
// console.log('ACCEPT ROW', row);
|
|
114
145
|
if (this.currentWriter) this.currentWriter.row(row);
|
|
115
146
|
else if (row.message) process.send({ msgtype: 'info', info: { message: row.message } });
|
|
116
147
|
// this.onRow(this.jslid);
|
|
@@ -135,6 +166,17 @@ function handleStream(driver, resultIndexHolder, sql) {
|
|
|
135
166
|
});
|
|
136
167
|
}
|
|
137
168
|
|
|
169
|
+
function allowExecuteCustomScript(driver) {
|
|
170
|
+
if (driver.readOnlySessions) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
if (storedConnection.isReadOnly) {
|
|
174
|
+
return false;
|
|
175
|
+
// throw new Error('Connection is read only');
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
138
180
|
async function handleConnect(connection) {
|
|
139
181
|
storedConnection = connection;
|
|
140
182
|
|
|
@@ -163,6 +205,19 @@ async function handleExecuteQuery({ sql }) {
|
|
|
163
205
|
await waitConnected();
|
|
164
206
|
const driver = requireEngineDriver(storedConnection);
|
|
165
207
|
|
|
208
|
+
if (!allowExecuteCustomScript(driver)) {
|
|
209
|
+
process.send({
|
|
210
|
+
msgtype: 'info',
|
|
211
|
+
info: {
|
|
212
|
+
message: 'Connection without read-only sessions is read only',
|
|
213
|
+
severity: 'error',
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
process.send({ msgtype: 'done', skipFinishedMessage: true });
|
|
217
|
+
return;
|
|
218
|
+
//process.send({ msgtype: 'error', error: e.message });
|
|
219
|
+
}
|
|
220
|
+
|
|
166
221
|
const resultIndexHolder = {
|
|
167
222
|
value: 0,
|
|
168
223
|
};
|
|
@@ -176,9 +231,39 @@ async function handleExecuteQuery({ sql }) {
|
|
|
176
231
|
process.send({ msgtype: 'done' });
|
|
177
232
|
}
|
|
178
233
|
|
|
234
|
+
async function handleExecuteReader({ jslid, sql, fileName }) {
|
|
235
|
+
await waitConnected();
|
|
236
|
+
|
|
237
|
+
const driver = requireEngineDriver(storedConnection);
|
|
238
|
+
|
|
239
|
+
if (fileName) {
|
|
240
|
+
sql = fs.readFileSync(fileName, 'utf-8');
|
|
241
|
+
} else {
|
|
242
|
+
if (!allowExecuteCustomScript(driver)) {
|
|
243
|
+
process.send({ msgtype: 'done' });
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const writer = new TableWriter();
|
|
249
|
+
writer.initializeFromReader(jslid);
|
|
250
|
+
|
|
251
|
+
const reader = await driver.readQuery(systemConnection, sql);
|
|
252
|
+
|
|
253
|
+
reader.on('data', data => {
|
|
254
|
+
writer.rowFromReader(data);
|
|
255
|
+
});
|
|
256
|
+
reader.on('end', () => {
|
|
257
|
+
writer.close(() => {
|
|
258
|
+
process.send({ msgtype: 'done' });
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
179
263
|
const messageHandlers = {
|
|
180
264
|
connect: handleConnect,
|
|
181
265
|
executeQuery: handleExecuteQuery,
|
|
266
|
+
executeReader: handleExecuteReader,
|
|
182
267
|
// cancel: handleCancel,
|
|
183
268
|
};
|
|
184
269
|
|
package/src/shell/tableReader.js
CHANGED
|
@@ -9,7 +9,7 @@ async function tableReader({ connection, pureName, schemaName }) {
|
|
|
9
9
|
|
|
10
10
|
const fullName = { pureName, schemaName };
|
|
11
11
|
|
|
12
|
-
if (driver.
|
|
12
|
+
if (driver.databaseEngineTypes.includes('document')) {
|
|
13
13
|
// @ts-ignore
|
|
14
14
|
console.log(`Reading collection ${fullNameToString(fullName)}`);
|
|
15
15
|
// @ts-ignore
|