dbgate-api-premium 6.2.0 → 6.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dbgate-api-premium",
3
3
  "main": "src/index.js",
4
- "version": "6.2.0",
4
+ "version": "6.3.0",
5
5
  "homepage": "https://dbgate.org/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -29,10 +29,10 @@
29
29
  "compare-versions": "^3.6.0",
30
30
  "cors": "^2.8.5",
31
31
  "cross-env": "^6.0.3",
32
- "dbgate-datalib": "^6.2.0",
32
+ "dbgate-datalib": "^6.3.0",
33
33
  "dbgate-query-splitter": "^4.11.3",
34
- "dbgate-sqltree": "^6.2.0",
35
- "dbgate-tools": "^6.2.0",
34
+ "dbgate-sqltree": "^6.3.0",
35
+ "dbgate-tools": "^6.3.0",
36
36
  "debug": "^4.3.4",
37
37
  "diff": "^5.0.0",
38
38
  "diff2html": "^3.4.13",
@@ -83,7 +83,7 @@
83
83
  "devDependencies": {
84
84
  "@types/fs-extra": "^9.0.11",
85
85
  "@types/lodash": "^4.14.149",
86
- "dbgate-types": "^6.2.0",
86
+ "dbgate-types": "^6.3.0",
87
87
  "env-cmd": "^10.1.0",
88
88
  "jsdoc-to-markdown": "^9.0.5",
89
89
  "node-loader": "^1.0.2",
@@ -43,6 +43,7 @@ function authMiddleware(req, res, next) {
43
43
  '/connections/dblogin-app',
44
44
  '/connections/dblogin-auth',
45
45
  '/connections/dblogin-auth-token',
46
+ '/health',
46
47
  ];
47
48
 
48
49
  // console.log('********************* getAuthProvider()', getAuthProvider());
@@ -62,7 +62,10 @@ function getPortalCollections() {
62
62
  port: process.env[`PORT_${id}`],
63
63
  databaseUrl: process.env[`URL_${id}`],
64
64
  useDatabaseUrl: !!process.env[`URL_${id}`],
65
- databaseFile: process.env[`FILE_${id}`],
65
+ databaseFile: process.env[`FILE_${id}`]?.replace(
66
+ '%%E2E_TEST_DATA_DIRECTORY%%',
67
+ path.join(path.dirname(path.dirname(__dirname)), 'e2e-tests', 'tmpdata')
68
+ ),
66
69
  socketPath: process.env[`SOCKET_PATH_${id}`],
67
70
  serviceName: process.env[`SERVICE_NAME_${id}`],
68
71
  authType: process.env[`AUTH_TYPE_${id}`] || (process.env[`SOCKET_PATH_${id}`] ? 'socket' : undefined),
@@ -77,6 +80,7 @@ function getPortalCollections() {
77
80
  allowedDatabasesRegex: process.env[`ALLOWED_DATABASES_REGEX_${id}`],
78
81
  parent: process.env[`PARENT_${id}`] || undefined,
79
82
  useSeparateSchemas: !!process.env[`USE_SEPARATE_SCHEMAS_${id}`],
83
+ localDataCenter: process.env[`LOCAL_DATA_CENTER_${id}`],
80
84
 
81
85
  // SSH tunnel
82
86
  useSshTunnel: process.env[`USE_SSH_${id}`],
@@ -1,4 +1,5 @@
1
1
  const connections = require('./connections');
2
+ const runners = require('./runners');
2
3
  const archive = require('./archive');
3
4
  const socket = require('../utility/socket');
4
5
  const { fork } = require('child_process');
@@ -233,6 +234,7 @@ module.exports = {
233
234
  dispatchDatabaseChangedEvent_meta: true,
234
235
  dispatchDatabaseChangedEvent({ event, conid, database }) {
235
236
  socket.emitChanged(event, { conid, database });
237
+ return null;
236
238
  },
237
239
 
238
240
  loadKeys_meta: true,
@@ -612,4 +614,129 @@ module.exports = {
612
614
 
613
615
  return res;
614
616
  },
617
+
618
+ async getNativeOpCommandArgs(
619
+ command,
620
+ { conid, database, outputFile, inputFile, options, selectedTables, skippedTables, argsFormat }
621
+ ) {
622
+ const connection = await connections.getCore({ conid });
623
+ const driver = requireEngineDriver(connection);
624
+
625
+ const settingsValue = await config.getSettings();
626
+
627
+ const externalTools = {};
628
+ for (const pair of Object.entries(settingsValue || {})) {
629
+ const [name, value] = pair;
630
+ if (name.startsWith('externalTools.')) {
631
+ externalTools[name.substring('externalTools.'.length)] = value;
632
+ }
633
+ }
634
+
635
+ return {
636
+ ...(command == 'backup'
637
+ ? driver.backupDatabaseCommand(
638
+ connection,
639
+ { outputFile, database, options, selectedTables, skippedTables, argsFormat },
640
+ // @ts-ignore
641
+ externalTools
642
+ )
643
+ : driver.restoreDatabaseCommand(
644
+ connection,
645
+ { inputFile, database, options, argsFormat },
646
+ // @ts-ignore
647
+ externalTools
648
+ )),
649
+ transformMessage: driver.transformNativeCommandMessage
650
+ ? message => driver.transformNativeCommandMessage(message, command)
651
+ : null,
652
+ };
653
+ },
654
+
655
+ commandArgsToCommandLine(commandArgs) {
656
+ const { command, args, stdinFilePath } = commandArgs;
657
+ let res = `${command} ${args.join(' ')}`;
658
+ if (stdinFilePath) {
659
+ res += ` < ${stdinFilePath}`;
660
+ }
661
+ return res;
662
+ },
663
+
664
+ nativeBackup_meta: true,
665
+ async nativeBackup({ conid, database, outputFile, runid, options, selectedTables, skippedTables }) {
666
+ const commandArgs = await this.getNativeOpCommandArgs('backup', {
667
+ conid,
668
+ database,
669
+ inputFile: undefined,
670
+ outputFile,
671
+ options,
672
+ selectedTables,
673
+ skippedTables,
674
+ argsFormat: 'spawn',
675
+ });
676
+
677
+ return runners.nativeRunCore(runid, {
678
+ ...commandArgs,
679
+ onFinished: () => {
680
+ socket.emitChanged(`files-changed`, { folder: 'sql' });
681
+ socket.emitChanged(`all-files-changed`);
682
+ },
683
+ });
684
+ },
685
+
686
+ nativeBackupCommand_meta: true,
687
+ async nativeBackupCommand({ conid, database, outputFile, options, selectedTables, skippedTables }) {
688
+ const commandArgs = await this.getNativeOpCommandArgs('backup', {
689
+ conid,
690
+ database,
691
+ outputFile,
692
+ inputFile: undefined,
693
+ options,
694
+ selectedTables,
695
+ skippedTables,
696
+ argsFormat: 'shell',
697
+ });
698
+
699
+ return {
700
+ ...commandArgs,
701
+ transformMessage: null,
702
+ commandLine: this.commandArgsToCommandLine(commandArgs),
703
+ };
704
+ },
705
+
706
+ nativeRestore_meta: true,
707
+ async nativeRestore({ conid, database, inputFile, runid }) {
708
+ const commandArgs = await this.getNativeOpCommandArgs('restore', {
709
+ conid,
710
+ database,
711
+ inputFile,
712
+ outputFile: undefined,
713
+ options: undefined,
714
+ argsFormat: 'spawn',
715
+ });
716
+
717
+ return runners.nativeRunCore(runid, {
718
+ ...commandArgs,
719
+ onFinished: () => {
720
+ this.syncModel({ conid, database, isFullRefresh: true });
721
+ },
722
+ });
723
+ },
724
+
725
+ nativeRestoreCommand_meta: true,
726
+ async nativeRestoreCommand({ conid, database, inputFile }) {
727
+ const commandArgs = await this.getNativeOpCommandArgs('restore', {
728
+ conid,
729
+ database,
730
+ inputFile,
731
+ outputFile: undefined,
732
+ options: undefined,
733
+ argsFormat: 'shell',
734
+ });
735
+
736
+ return {
737
+ ...commandArgs,
738
+ transformMessage: null,
739
+ commandLine: this.commandArgsToCommandLine(commandArgs),
740
+ };
741
+ },
615
742
  };
@@ -4,7 +4,7 @@ const path = require('path');
4
4
  const fs = require('fs-extra');
5
5
  const byline = require('byline');
6
6
  const socket = require('../utility/socket');
7
- const { fork } = require('child_process');
7
+ const { fork, spawn } = require('child_process');
8
8
  const { rundir, uploadsdir, pluginsdir, getPluginBackendPath, packagedPluginList } = require('../utility/directories');
9
9
  const {
10
10
  extractShellApiPlugins,
@@ -13,6 +13,8 @@ const {
13
13
  getLogger,
14
14
  safeJsonParse,
15
15
  pinoLogRecordToMessageRecord,
16
+ extractErrorMessage,
17
+ extractErrorLogData,
16
18
  } = require('dbgate-tools');
17
19
  const { handleProcessCommunication } = require('../utility/processComm');
18
20
  const processArgs = require('../utility/processArgs');
@@ -80,6 +82,7 @@ module.exports = {
80
82
  }
81
83
  : {
82
84
  message,
85
+ severity: 'info',
83
86
  time: new Date(),
84
87
  };
85
88
 
@@ -94,14 +97,26 @@ module.exports = {
94
97
  handle_ping() {},
95
98
 
96
99
  handle_freeData(runid, { freeData }) {
97
- const [resolve, reject] = this.requests[runid];
100
+ const { resolve } = this.requests[runid];
98
101
  resolve(freeData);
99
102
  delete this.requests[runid];
100
103
  },
101
104
 
105
+ handle_copyStreamError(runid, { copyStreamError }) {
106
+ const { reject, exitOnStreamError } = this.requests[runid] || {};
107
+ if (exitOnStreamError) {
108
+ reject(copyStreamError);
109
+ delete this.requests[runid];
110
+ }
111
+ },
112
+
113
+ handle_progress(runid, progressData) {
114
+ socket.emit(`runner-progress-${runid}`, progressData);
115
+ },
116
+
102
117
  rejectRequest(runid, error) {
103
118
  if (this.requests[runid]) {
104
- const [resolve, reject] = this.requests[runid];
119
+ const { reject } = this.requests[runid];
105
120
  reject(error);
106
121
  delete this.requests[runid];
107
122
  }
@@ -113,6 +128,8 @@ module.exports = {
113
128
  fs.writeFileSync(`${scriptFile}`, scriptText);
114
129
  fs.mkdirSync(directory);
115
130
  const pluginNames = extractPlugins(scriptText);
131
+ // console.log('********************** SCRIPT TEXT **********************');
132
+ // console.log(scriptText);
116
133
  logger.info({ scriptFile }, 'Running script');
117
134
  // const subprocess = fork(scriptFile, ['--checkParent', '--max-old-space-size=8192'], {
118
135
  const subprocess = fork(
@@ -150,17 +167,21 @@ module.exports = {
150
167
  byline(subprocess.stdout).on('data', pipeDispatcher('info'));
151
168
  byline(subprocess.stderr).on('data', pipeDispatcher('error'));
152
169
  subprocess.on('exit', code => {
170
+ // console.log('... EXITED', code);
153
171
  this.rejectRequest(runid, { message: 'No data returned, maybe input data source is too big' });
154
172
  logger.info({ code, pid: subprocess.pid }, 'Exited process');
155
173
  socket.emit(`runner-done-${runid}`, code);
174
+ this.opened = this.opened.filter(x => x.runid != runid);
156
175
  });
157
176
  subprocess.on('error', error => {
177
+ // console.log('... ERROR subprocess', error);
158
178
  this.rejectRequest(runid, { message: error && (error.message || error.toString()) });
159
179
  console.error('... ERROR subprocess', error);
160
- this.dispatchMessage({
180
+ this.dispatchMessage(runid, {
161
181
  severity: 'error',
162
182
  message: error.toString(),
163
183
  });
184
+ this.opened = this.opened.filter(x => x.runid != runid);
164
185
  });
165
186
  const newOpened = {
166
187
  runid,
@@ -176,6 +197,77 @@ module.exports = {
176
197
  return _.pick(newOpened, ['runid']);
177
198
  },
178
199
 
200
+ nativeRunCore(runid, commandArgs) {
201
+ const { command, args, env, transformMessage, stdinFilePath, onFinished } = commandArgs;
202
+ const pipeDispatcher = severity => data => {
203
+ let messageObject = {
204
+ message: data.toString().trim(),
205
+ severity,
206
+ };
207
+ if (transformMessage) {
208
+ messageObject = transformMessage(messageObject);
209
+ }
210
+
211
+ if (messageObject) {
212
+ return this.dispatchMessage(runid, messageObject);
213
+ }
214
+ };
215
+
216
+ const subprocess = spawn(command, args, { env: { ...process.env, ...env } });
217
+
218
+ byline(subprocess.stdout).on('data', pipeDispatcher('info'));
219
+ byline(subprocess.stderr).on('data', pipeDispatcher('error'));
220
+
221
+ subprocess.on('exit', code => {
222
+ console.log('... EXITED', code);
223
+ logger.info({ code, pid: subprocess.pid }, 'Exited process');
224
+ this.dispatchMessage(runid, `Finished external process with code ${code}`);
225
+ socket.emit(`runner-done-${runid}`, code);
226
+ if (onFinished) {
227
+ onFinished();
228
+ }
229
+ this.opened = this.opened.filter(x => x.runid != runid);
230
+ });
231
+ subprocess.on('spawn', () => {
232
+ this.dispatchMessage(runid, `Started external process ${command}`);
233
+ });
234
+ subprocess.on('error', error => {
235
+ console.log('... ERROR subprocess', error);
236
+ this.dispatchMessage(runid, {
237
+ severity: 'error',
238
+ message: error.toString(),
239
+ });
240
+ if (error['code'] == 'ENOENT') {
241
+ this.dispatchMessage(runid, {
242
+ severity: 'error',
243
+ message: `Command ${command} not found, please install it and configure its location in DbGate settings, Settings/External tools, if ${command} is not in system PATH`,
244
+ });
245
+ }
246
+ socket.emit(`runner-done-${runid}`);
247
+ this.opened = this.opened.filter(x => x.runid != runid);
248
+ });
249
+
250
+ if (stdinFilePath) {
251
+ const inputStream = fs.createReadStream(stdinFilePath);
252
+ inputStream.pipe(subprocess.stdin);
253
+
254
+ subprocess.stdin.on('error', err => {
255
+ this.dispatchMessage(runid, {
256
+ severity: 'error',
257
+ message: extractErrorMessage(err),
258
+ });
259
+ logger.error(extractErrorLogData(err), 'Caught error on stdin');
260
+ });
261
+ }
262
+
263
+ const newOpened = {
264
+ runid,
265
+ subprocess,
266
+ };
267
+ this.opened.push(newOpened);
268
+ return _.pick(newOpened, ['runid']);
269
+ },
270
+
179
271
  start_meta: true,
180
272
  async start({ script }) {
181
273
  const runid = crypto.randomUUID();
@@ -231,7 +323,7 @@ module.exports = {
231
323
 
232
324
  const promise = new Promise((resolve, reject) => {
233
325
  const runid = crypto.randomUUID();
234
- this.requests[runid] = [resolve, reject];
326
+ this.requests[runid] = { resolve, reject, exitOnStreamError: true };
235
327
  this.startCore(runid, loaderScriptTemplate(prefix, functionName, props, runid));
236
328
  });
237
329
  return promise;
@@ -56,12 +56,19 @@ module.exports = {
56
56
  handle_done(sesid, props) {
57
57
  socket.emit(`session-done-${sesid}`);
58
58
  if (!props.skipFinishedMessage) {
59
- this.dispatchMessage(sesid, 'Query execution finished');
59
+ if (props.controlCommand) {
60
+ this.dispatchMessage(sesid, `${_.startCase(props.controlCommand)} finished`);
61
+ } else {
62
+ this.dispatchMessage(sesid, 'Query execution finished');
63
+ }
60
64
  }
61
65
  const session = this.opened.find(x => x.sesid == sesid);
62
66
  if (session.loadingReader_jslid) {
63
67
  socket.emit(`session-jslid-done-${session.loadingReader_jslid}`);
64
68
  }
69
+ if (props.autoCommit) {
70
+ this.executeControlCommand({ sesid, command: 'commitTransaction' });
71
+ }
65
72
  if (session.killOnDone) {
66
73
  this.kill({ sesid });
67
74
  }
@@ -120,6 +127,9 @@ module.exports = {
120
127
  this.dispatchMessage(sesid, 'Query session closed');
121
128
  socket.emit(`session-closed-${sesid}`);
122
129
  });
130
+ subprocess.on('error', () => {
131
+ this.opened = this.opened.filter(x => x.sesid != sesid);
132
+ });
123
133
 
124
134
  subprocess.send({
125
135
  msgtype: 'connect',
@@ -131,7 +141,7 @@ module.exports = {
131
141
  },
132
142
 
133
143
  executeQuery_meta: true,
134
- async executeQuery({ sesid, sql }) {
144
+ async executeQuery({ sesid, sql, autoCommit }) {
135
145
  const session = this.opened.find(x => x.sesid == sesid);
136
146
  if (!session) {
137
147
  throw new Error('Invalid session');
@@ -139,7 +149,21 @@ module.exports = {
139
149
 
140
150
  logger.info({ sesid, sql }, 'Processing query');
141
151
  this.dispatchMessage(sesid, 'Query execution started');
142
- session.subprocess.send({ msgtype: 'executeQuery', sql });
152
+ session.subprocess.send({ msgtype: 'executeQuery', sql, autoCommit });
153
+
154
+ return { state: 'ok' };
155
+ },
156
+
157
+ executeControlCommand_meta: true,
158
+ async executeControlCommand({ sesid, command }) {
159
+ const session = this.opened.find(x => x.sesid == sesid);
160
+ if (!session) {
161
+ throw new Error('Invalid session');
162
+ }
163
+
164
+ logger.info({ sesid, command }, 'Processing control command');
165
+ this.dispatchMessage(sesid, `${_.startCase(command)} started`);
166
+ session.subprocess.send({ msgtype: 'executeControlCommand', command });
143
167
 
144
168
  return { state: 'ok' };
145
169
  },
@@ -1,6 +1,6 @@
1
1
  const crypto = require('crypto');
2
2
  const path = require('path');
3
- const { uploadsdir, getLogsFilePath } = require('../utility/directories');
3
+ const { uploadsdir, getLogsFilePath, filesdir } = require('../utility/directories');
4
4
  const { getLogger, extractErrorLogData } = require('dbgate-tools');
5
5
  const logger = getLogger('uploads');
6
6
  const axios = require('axios');
@@ -13,6 +13,7 @@ const serverConnections = require('./serverConnections');
13
13
  const config = require('./config');
14
14
  const gistSecret = require('../gistSecret');
15
15
  const currentVersion = require('../currentVersion');
16
+ const socket = require('../utility/socket');
16
17
 
17
18
  module.exports = {
18
19
  upload_meta: {
@@ -38,6 +39,52 @@ module.exports = {
38
39
  });
39
40
  },
40
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
+
41
88
  get_meta: {
42
89
  method: 'get',
43
90
  raw: true,
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '6.2.0',
4
- buildTime: '2025-02-14T13:58:57.650Z'
3
+ version: '6.3.0',
4
+ buildTime: '2025-03-18T11:15:02.094Z'
5
5
  };
package/src/main.js CHANGED
@@ -30,7 +30,7 @@ const queryHistory = require('./controllers/queryHistory');
30
30
  const onFinished = require('on-finished');
31
31
  const processArgs = require('./utility/processArgs');
32
32
 
33
- const { rundir } = require('./utility/directories');
33
+ const { rundir, filesdir } = require('./utility/directories');
34
34
  const platformInfo = require('./utility/platformInfo');
35
35
  const getExpressPath = require('./utility/getExpressPath');
36
36
  const _ = require('lodash');
@@ -38,6 +38,7 @@ const { getLogger } = require('dbgate-tools');
38
38
  const { getDefaultAuthProvider } = require('./auth/authProvider');
39
39
  const startCloudUpgradeTimer = require('./utility/cloudUpgrade');
40
40
  const { isProApp } = require('./utility/checkLicense');
41
+ const getHealthStatus = require('./utility/healthStatus');
41
42
 
42
43
  const logger = getLogger('main');
43
44
 
@@ -117,6 +118,12 @@ function start() {
117
118
  });
118
119
  });
119
120
 
121
+ app.get(getExpressPath('/health'), async function (req, res) {
122
+ res.setHeader('Content-Type', 'application/json');
123
+ const health = await getHealthStatus();
124
+ res.end(JSON.stringify(health, null, 2));
125
+ });
126
+
120
127
  app.use(bodyParser.json({ limit: '50mb' }));
121
128
 
122
129
  app.use(
@@ -133,6 +140,7 @@ function start() {
133
140
  // }
134
141
 
135
142
  app.use(getExpressPath('/runners/data'), express.static(rundir()));
143
+ app.use(getExpressPath('/files/data'), express.static(filesdir()));
136
144
 
137
145
  if (platformInfo.isDocker) {
138
146
  const port = process.env.PORT || 3000;
@@ -245,7 +245,47 @@ async function handleStopProfiler({ jslid }) {
245
245
  currentProfiler = null;
246
246
  }
247
247
 
248
- async function handleExecuteQuery({ sql }) {
248
+ async function handleExecuteControlCommand({ command }) {
249
+ lastActivity = new Date().getTime();
250
+
251
+ await waitConnected();
252
+ const driver = requireEngineDriver(storedConnection);
253
+
254
+ if (command == 'commitTransaction' && !allowExecuteCustomScript(driver)) {
255
+ process.send({
256
+ msgtype: 'info',
257
+ info: {
258
+ message: 'Connection without read-only sessions is read only',
259
+ severity: 'error',
260
+ },
261
+ });
262
+ process.send({ msgtype: 'done', skipFinishedMessage: true });
263
+ return;
264
+ //process.send({ msgtype: 'error', error: e.message });
265
+ }
266
+
267
+ executingScripts++;
268
+ try {
269
+ const dmp = driver.createDumper();
270
+ switch (command) {
271
+ case 'commitTransaction':
272
+ await dmp.commitTransaction();
273
+ break;
274
+ case 'rollbackTransaction':
275
+ await dmp.rollbackTransaction();
276
+ break;
277
+ case 'beginTransaction':
278
+ await dmp.beginTransaction();
279
+ break;
280
+ }
281
+ await driver.query(dbhan, dmp.s, { discardResult: true });
282
+ process.send({ msgtype: 'done', controlCommand: command });
283
+ } finally {
284
+ executingScripts--;
285
+ }
286
+ }
287
+
288
+ async function handleExecuteQuery({ sql, autoCommit }) {
249
289
  lastActivity = new Date().getTime();
250
290
 
251
291
  await waitConnected();
@@ -279,7 +319,7 @@ async function handleExecuteQuery({ sql }) {
279
319
  // handler.stream = stream;
280
320
  // resultIndex = handler.resultIndex;
281
321
  }
282
- process.send({ msgtype: 'done' });
322
+ process.send({ msgtype: 'done', autoCommit });
283
323
  } finally {
284
324
  executingScripts--;
285
325
  }
@@ -323,6 +363,7 @@ function handlePing() {
323
363
  const messageHandlers = {
324
364
  connect: handleConnect,
325
365
  executeQuery: handleExecuteQuery,
366
+ executeControlCommand: handleExecuteControlCommand,
326
367
  executeReader: handleExecuteReader,
327
368
  startProfiler: handleStartProfiler,
328
369
  stopProfiler: handleStopProfiler,
@@ -1,6 +1,27 @@
1
1
  const EnsureStreamHeaderStream = require('../utility/EnsureStreamHeaderStream');
2
- const Stream = require('stream');
3
2
  const ColumnMapTransformStream = require('../utility/ColumnMapTransformStream');
3
+ const streamPipeline = require('../utility/streamPipeline');
4
+ const { getLogger, extractErrorLogData, RowProgressReporter, extractErrorMessage } = require('dbgate-tools');
5
+ const logger = getLogger('copyStream');
6
+ const stream = require('stream');
7
+
8
+ class ReportingTransform extends stream.Transform {
9
+ constructor(reporter, options = {}) {
10
+ super({ ...options, objectMode: true });
11
+ this.reporter = reporter;
12
+ }
13
+ _transform(chunk, encoding, callback) {
14
+ if (!chunk?.__isStreamHeader) {
15
+ this.reporter.add(1);
16
+ }
17
+ this.push(chunk);
18
+ callback();
19
+ }
20
+ _flush(callback) {
21
+ this.reporter.finish();
22
+ callback();
23
+ }
24
+ }
4
25
 
5
26
  /**
6
27
  * Copies reader to writer. Used for import, export tables and transfer data between tables
@@ -9,10 +30,23 @@ const ColumnMapTransformStream = require('../utility/ColumnMapTransformStream');
9
30
  * @param {object} options - options
10
31
  * @returns {Promise}
11
32
  */
12
- function copyStream(input, output, options) {
13
- const { columns } = options || {};
33
+ async function copyStream(input, output, options) {
34
+ const { columns, progressName } = options || {};
35
+
36
+ if (progressName) {
37
+ process.send({
38
+ msgtype: 'progress',
39
+ progressName,
40
+ status: 'running',
41
+ });
42
+ }
14
43
 
15
44
  const transforms = [];
45
+
46
+ if (progressName) {
47
+ const reporter = new RowProgressReporter(progressName, 'readRowCount');
48
+ transforms.push(new ReportingTransform(reporter));
49
+ }
16
50
  if (columns) {
17
51
  transforms.push(new ColumnMapTransformStream(columns));
18
52
  }
@@ -20,36 +54,37 @@ function copyStream(input, output, options) {
20
54
  transforms.push(new EnsureStreamHeaderStream());
21
55
  }
22
56
 
23
- // return new Promise((resolve, reject) => {
24
- // Stream.pipeline(input, ...transforms, output, err => {
25
- // if (err) {
26
- // reject(err);
27
- // } else {
28
- // resolve();
29
- // }
30
- // });
31
- // });
32
-
33
- return new Promise((resolve, reject) => {
34
- const finisher = output['finisher'] || output;
35
- finisher.on('finish', resolve);
36
- finisher.on('error', reject);
37
-
38
- let lastStream = input;
39
- for (const tran of transforms) {
40
- lastStream.pipe(tran);
41
- lastStream = tran;
57
+ try {
58
+ await streamPipeline(input, transforms, output);
59
+
60
+ if (progressName) {
61
+ process.send({
62
+ msgtype: 'progress',
63
+ progressName,
64
+ status: 'done',
65
+ });
42
66
  }
43
- lastStream.pipe(output);
44
-
45
- // if (output.requireFixedStructure) {
46
- // const ensureHeader = new EnsureStreamHeaderStream();
47
- // input.pipe(ensureHeader);
48
- // ensureHeader.pipe(output);
49
- // } else {
50
- // input.pipe(output);
51
- // }
52
- });
67
+ } catch (err) {
68
+ process.send({
69
+ msgtype: 'copyStreamError',
70
+ copyStreamError: {
71
+ message: extractErrorMessage(err),
72
+ ...err,
73
+ },
74
+ });
75
+
76
+ if (progressName) {
77
+ process.send({
78
+ msgtype: 'progress',
79
+ progressName,
80
+ status: 'error',
81
+ errorMessage: extractErrorMessage(err),
82
+ });
83
+ }
84
+
85
+ logger.error(extractErrorLogData(err, { progressName }), 'Import/export job failed');
86
+ // throw err;
87
+ }
53
88
  }
54
89
 
55
90
  module.exports = copyStream;
@@ -24,8 +24,6 @@ async function dataDuplicator({
24
24
  const dbhan = systemConnection || (await connectUtility(driver, connection, 'write'));
25
25
 
26
26
  try {
27
- logger.info(`Connected.`);
28
-
29
27
  if (!analysedStructure) {
30
28
  analysedStructure = await driver.analyseFull(dbhan);
31
29
  }
@@ -19,8 +19,6 @@ async function dropAllDbObjects({ connection, systemConnection, driver, analysed
19
19
 
20
20
  const dbhan = systemConnection || (await connectUtility(driver, connection, 'write'));
21
21
 
22
- logger.info(`Connected.`);
23
-
24
22
  if (!analysedStructure) {
25
23
  analysedStructure = await driver.analyseFull(dbhan);
26
24
  }
@@ -36,7 +36,7 @@ async function executeQuery({
36
36
  }
37
37
 
38
38
  try {
39
- logger.info(`Connected.`);
39
+ logger.debug(`Running SQL query, length: ${sql.length}`);
40
40
 
41
41
  await driver.script(dbhan, sql, { logScriptItems });
42
42
  } finally {
@@ -5,6 +5,7 @@ const { splitQueryStream } = require('dbgate-query-splitter/lib/splitQueryStream
5
5
  const download = require('./download');
6
6
  const stream = require('stream');
7
7
  const { getLogger } = require('dbgate-tools');
8
+ const streamPipeline = require('../utility/streamPipeline');
8
9
 
9
10
  const logger = getLogger('importDb');
10
11
 
@@ -43,25 +44,12 @@ class ImportStream extends stream.Transform {
43
44
  }
44
45
  }
45
46
 
46
- function awaitStreamEnd(stream) {
47
- return new Promise((resolve, reject) => {
48
- stream.once('end', () => {
49
- resolve(true);
50
- });
51
- stream.once('error', err => {
52
- reject(err);
53
- });
54
- });
55
- }
56
-
57
47
  async function importDatabase({ connection = undefined, systemConnection = undefined, driver = undefined, inputFile }) {
58
48
  logger.info(`Importing database`);
59
49
 
60
50
  if (!driver) driver = requireEngineDriver(connection);
61
51
  const dbhan = systemConnection || (await connectUtility(driver, connection, 'write'));
62
52
  try {
63
- logger.info(`Connected.`);
64
-
65
53
  logger.info(`Input file: ${inputFile}`);
66
54
  const downloadedFile = await download(inputFile);
67
55
  logger.info(`Downloaded file: ${downloadedFile}`);
@@ -72,9 +60,8 @@ async function importDatabase({ connection = undefined, systemConnection = undef
72
60
  returnRichInfo: true,
73
61
  });
74
62
  const importStream = new ImportStream(dbhan, driver);
75
- // @ts-ignore
76
- splittedStream.pipe(importStream);
77
- await awaitStreamEnd(importStream);
63
+
64
+ await streamPipeline(splittedStream, importStream);
78
65
  } finally {
79
66
  if (!systemConnection) {
80
67
  await driver.close(dbhan);
@@ -0,0 +1,125 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const executeQuery = require('./executeQuery');
4
+ const { connectUtility } = require('../utility/connectUtility');
5
+ const requireEngineDriver = require('../utility/requireEngineDriver');
6
+ const { getAlterDatabaseScript, DatabaseAnalyser, runCommandOnDriver } = require('dbgate-tools');
7
+ const importDbModel = require('../utility/importDbModel');
8
+ const jsonLinesReader = require('./jsonLinesReader');
9
+ const tableWriter = require('./tableWriter');
10
+ const copyStream = require('./copyStream');
11
+
12
+ /**
13
+ * Deploys database model stored in modelFolder (table as yamls) to database
14
+ * @param {object} options
15
+ * @param {connectionType} options.connection - connection object
16
+ * @param {object} options.systemConnection - system connection (result of driver.connect). If not provided, new connection will be created
17
+ * @param {object} options.driver - driver object. If not provided, it will be loaded from connection
18
+ * @param {string} options.folder - folder with model files (YAML files for tables, SQL files for views, procedures, ...)
19
+ * @param {function[]} options.modelTransforms - array of functions for transforming model
20
+ */
21
+ async function importDbFromFolder({ connection, systemConnection, driver, folder, modelTransforms }) {
22
+ if (!driver) driver = requireEngineDriver(connection);
23
+ const dbhan = systemConnection || (await connectUtility(driver, connection, 'read'));
24
+
25
+ try {
26
+ if (driver?.databaseEngineTypes?.includes('sql')) {
27
+ const model = await importDbModel(folder);
28
+
29
+ let modelAdapted = {
30
+ ...model,
31
+ tables: model.tables.map(table => driver.adaptTableInfo(table)),
32
+ };
33
+ for (const transform of modelTransforms || []) {
34
+ modelAdapted = transform(modelAdapted);
35
+ }
36
+
37
+ const modelNoFk = {
38
+ ...modelAdapted,
39
+ tables: modelAdapted.tables.map(table => ({
40
+ ...table,
41
+ foreignKeys: [],
42
+ })),
43
+ };
44
+
45
+ // const plan = createAlterDatabasePlan(
46
+ // DatabaseAnalyser.createEmptyStructure(),
47
+ // driver.dialect.enableAllForeignKeys ? modelAdapted : modelNoFk,
48
+ // {},
49
+ // DatabaseAnalyser.createEmptyStructure(),
50
+ // driver.dialect.enableAllForeignKeys ? modelAdapted : modelNoFk,
51
+ // driver
52
+ // );
53
+ // const dmp1 = driver.createDumper({ useHardSeparator: true });
54
+ // if (driver.dialect.enableAllForeignKeys) {
55
+ // dmp1.enableAllForeignKeys(false);
56
+ // }
57
+ // plan.run(dmp1);
58
+ // if (driver.dialect.enableAllForeignKeys) {
59
+ // dmp1.enableAllForeignKeys(true);
60
+ // }
61
+
62
+ const { sql } = getAlterDatabaseScript(
63
+ DatabaseAnalyser.createEmptyStructure(),
64
+ driver.dialect.enableAllForeignKeys ? modelAdapted : modelNoFk,
65
+ {},
66
+ DatabaseAnalyser.createEmptyStructure(),
67
+ driver.dialect.enableAllForeignKeys ? modelAdapted : modelNoFk,
68
+ driver
69
+ );
70
+ // console.log('CREATING STRUCTURE:', sql);
71
+ await executeQuery({ connection, systemConnection: dbhan, driver, sql, logScriptItems: true });
72
+
73
+ if (driver.dialect.enableAllForeignKeys) {
74
+ await runCommandOnDriver(dbhan, driver, dmp => dmp.enableAllForeignKeys(false));
75
+ }
76
+
77
+ for (const table of modelAdapted.tables) {
78
+ const fileName = path.join(folder, `${table.pureName}.jsonl`);
79
+ if (await fs.exists(fileName)) {
80
+ const src = await jsonLinesReader({ fileName });
81
+ const dst = await tableWriter({
82
+ systemConnection: dbhan,
83
+ pureName: table.pureName,
84
+ driver,
85
+ targetTableStructure: table,
86
+ });
87
+ await copyStream(src, dst);
88
+ }
89
+ }
90
+
91
+ if (driver.dialect.enableAllForeignKeys) {
92
+ await runCommandOnDriver(dbhan, driver, dmp => dmp.enableAllForeignKeys(true));
93
+ } else if (driver.dialect.createForeignKey) {
94
+ const dmp = driver.createDumper();
95
+ for (const table of modelAdapted.tables) {
96
+ for (const fk of table.foreignKeys) {
97
+ dmp.createForeignKey(fk);
98
+ }
99
+ }
100
+
101
+ // create foreign keys
102
+ await executeQuery({ connection, systemConnection: dbhan, driver, sql: dmp.s, logScriptItems: true });
103
+ }
104
+ } else if (driver?.databaseEngineTypes?.includes('document')) {
105
+ for (const file of fs.readdirSync(folder)) {
106
+ if (!file.endsWith('.jsonl')) continue;
107
+ const pureName = path.parse(file).name;
108
+ const src = await jsonLinesReader({ fileName: path.join(folder, file) });
109
+ const dst = await tableWriter({
110
+ systemConnection: dbhan,
111
+ pureName,
112
+ driver,
113
+ createIfNotExists: true,
114
+ });
115
+ await copyStream(src, dst);
116
+ }
117
+ }
118
+ } finally {
119
+ if (!systemConnection) {
120
+ await driver.close(dbhan);
121
+ }
122
+ }
123
+ }
124
+
125
+ module.exports = importDbFromFolder;
@@ -21,7 +21,6 @@ const executeQuery = require('./executeQuery');
21
21
  const loadFile = require('./loadFile');
22
22
  const deployDb = require('./deployDb');
23
23
  const initializeApiEnvironment = require('./initializeApiEnvironment');
24
- const dumpDatabase = require('./dumpDatabase');
25
24
  const importDatabase = require('./importDatabase');
26
25
  const loadDatabase = require('./loadDatabase');
27
26
  const generateModelSql = require('./generateModelSql');
@@ -35,6 +34,7 @@ const sqlTextReplacementTransform = require('./sqlTextReplacementTransform');
35
34
  const autoIndexForeignKeysTransform = require('./autoIndexForeignKeysTransform');
36
35
  const generateDeploySql = require('./generateDeploySql');
37
36
  const dropAllDbObjects = require('./dropAllDbObjects');
37
+ const importDbFromFolder = require('./importDbFromFolder');
38
38
 
39
39
  const dbgateApi = {
40
40
  queryReader,
@@ -60,7 +60,6 @@ const dbgateApi = {
60
60
  loadFile,
61
61
  deployDb,
62
62
  initializeApiEnvironment,
63
- dumpDatabase,
64
63
  importDatabase,
65
64
  loadDatabase,
66
65
  generateModelSql,
@@ -73,6 +72,7 @@ const dbgateApi = {
73
72
  autoIndexForeignKeysTransform,
74
73
  generateDeploySql,
75
74
  dropAllDbObjects,
75
+ importDbFromFolder,
76
76
  };
77
77
 
78
78
  requirePlugin.initializeDbgateApi(dbgateApi);
@@ -53,8 +53,7 @@ async function jsonLinesReader({ fileName, encoding = 'utf-8', limitRows = undef
53
53
  );
54
54
  const liner = byline(fileStream);
55
55
  const parser = new ParseStream({ limitRows });
56
- liner.pipe(parser);
57
- return parser;
56
+ return [liner, parser];
58
57
  }
59
58
 
60
59
  module.exports = jsonLinesReader;
@@ -10,7 +10,6 @@ const download = require('./download');
10
10
 
11
11
  const logger = getLogger('jsonReader');
12
12
 
13
-
14
13
  class ParseStream extends stream.Transform {
15
14
  constructor({ limitRows, jsonStyle, keyField }) {
16
15
  super({ objectMode: true });
@@ -72,8 +71,12 @@ async function jsonReader({
72
71
  // @ts-ignore
73
72
  encoding
74
73
  );
74
+
75
75
  const parseJsonStream = parser();
76
- fileStream.pipe(parseJsonStream);
76
+
77
+ const resultPipe = [fileStream, parseJsonStream];
78
+
79
+ // fileStream.pipe(parseJsonStream);
77
80
 
78
81
  const parseStream = new ParseStream({ limitRows, jsonStyle, keyField });
79
82
 
@@ -81,15 +84,20 @@ async function jsonReader({
81
84
 
82
85
  if (rootField) {
83
86
  const filterStream = pick({ filter: rootField });
84
- parseJsonStream.pipe(filterStream);
85
- filterStream.pipe(tramsformer);
86
- } else {
87
- parseJsonStream.pipe(tramsformer);
87
+ resultPipe.push(filterStream);
88
+ // parseJsonStream.pipe(filterStream);
89
+ // filterStream.pipe(tramsformer);
88
90
  }
91
+ // else {
92
+ // parseJsonStream.pipe(tramsformer);
93
+ // }
94
+
95
+ resultPipe.push(tramsformer);
96
+ resultPipe.push(parseStream);
89
97
 
90
- tramsformer.pipe(parseStream);
98
+ // tramsformer.pipe(parseStream);
91
99
 
92
- return parseStream;
100
+ return resultPipe;
93
101
  }
94
102
 
95
103
  module.exports = jsonReader;
@@ -99,9 +99,10 @@ async function jsonWriter({ fileName, jsonStyle, keyField = '_key', rootField, e
99
99
  logger.info(`Writing file ${fileName}`);
100
100
  const stringify = new StringifyStream({ jsonStyle, keyField, rootField });
101
101
  const fileStream = fs.createWriteStream(fileName, encoding);
102
- stringify.pipe(fileStream);
103
- stringify['finisher'] = fileStream;
104
- return stringify;
102
+ return [stringify, fileStream];
103
+ // stringify.pipe(fileStream);
104
+ // stringify['finisher'] = fileStream;
105
+ // return stringify;
105
106
  }
106
107
 
107
108
  module.exports = jsonWriter;
@@ -6,15 +6,13 @@ const exportDbModel = require('../utility/exportDbModel');
6
6
  const logger = getLogger('analyseDb');
7
7
 
8
8
  async function loadDatabase({ connection = undefined, systemConnection = undefined, driver = undefined, outputDir }) {
9
- logger.info(`Analysing database`);
9
+ logger.debug(`Analysing database`);
10
10
 
11
11
  if (!driver) driver = requireEngineDriver(connection);
12
12
  const dbhan = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
13
13
  try {
14
- logger.info(`Connected.`);
15
-
16
14
  const dbInfo = await driver.analyseFull(dbhan);
17
- logger.info(`Analyse finished`);
15
+ logger.debug(`Analyse finished`);
18
16
 
19
17
  await exportDbModel(dbInfo, outputDir);
20
18
  } finally {
@@ -141,8 +141,9 @@ async function modifyJsonLinesReader({
141
141
  );
142
142
  const liner = byline(fileStream);
143
143
  const parser = new ParseStream({ limitRows, changeSet, mergedRows, mergeKey, mergeMode });
144
- liner.pipe(parser);
145
- return parser;
144
+ return [liner, parser];
145
+ // liner.pipe(parser);
146
+ // return parser;
146
147
  }
147
148
 
148
149
  module.exports = modifyJsonLinesReader;
@@ -30,7 +30,6 @@ async function queryReader({
30
30
 
31
31
  const driver = requireEngineDriver(connection);
32
32
  const pool = await connectUtility(driver, connection, queryType == 'json' ? 'read' : 'script');
33
- logger.info(`Connected.`);
34
33
  const reader =
35
34
  queryType == 'json' ? await driver.readJsonQuery(pool, query) : await driver.readQuery(pool, query || sql);
36
35
  return reader;
@@ -44,9 +44,10 @@ async function sqlDataWriter({ fileName, dataName, driver, encoding = 'utf-8' })
44
44
  logger.info(`Writing file ${fileName}`);
45
45
  const stringify = new SqlizeStream({ fileName, dataName });
46
46
  const fileStream = fs.createWriteStream(fileName, encoding);
47
- stringify.pipe(fileStream);
48
- stringify['finisher'] = fileStream;
49
- return stringify;
47
+ return [stringify, fileStream];
48
+ // stringify.pipe(fileStream);
49
+ // stringify['finisher'] = fileStream;
50
+ // return stringify;
50
51
  }
51
52
 
52
53
  module.exports = sqlDataWriter;
@@ -18,7 +18,6 @@ async function tableReader({ connection, systemConnection, pureName, schemaName,
18
18
  driver = requireEngineDriver(connection);
19
19
  }
20
20
  const dbhan = systemConnection || (await connectUtility(driver, connection, 'read'));
21
- logger.info(`Connected.`);
22
21
 
23
22
  const fullName = { pureName, schemaName };
24
23
 
@@ -15,6 +15,8 @@ const logger = getLogger('tableWriter');
15
15
  * @param {boolean} options.truncate - truncate table before insert
16
16
  * @param {boolean} options.createIfNotExists - create table if not exists
17
17
  * @param {boolean} options.commitAfterInsert - commit transaction after insert
18
+ * @param {string} options.progressName - name for reporting progress
19
+ * @param {any} options.targetTableStructure - target table structure (don't analyse if given)
18
20
  * @returns {Promise<writerType>} - writer object
19
21
  */
20
22
  async function tableWriter({ connection, schemaName, pureName, driver, systemConnection, ...options }) {
@@ -25,8 +27,20 @@ async function tableWriter({ connection, schemaName, pureName, driver, systemCon
25
27
  }
26
28
  const dbhan = systemConnection || (await connectUtility(driver, connection, 'write'));
27
29
 
28
- logger.info(`Connected.`);
29
- return await driver.writeTable(dbhan, { schemaName, pureName }, options);
30
+ try {
31
+ return await driver.writeTable(dbhan, { schemaName, pureName }, options);
32
+ } catch (err) {
33
+ if (options.progressName) {
34
+ process.send({
35
+ msgtype: 'progress',
36
+ progressName: options.progressName,
37
+ status: 'error',
38
+ errorMessage: err.message,
39
+ });
40
+ }
41
+
42
+ throw err;
43
+ }
30
44
  }
31
45
 
32
46
  module.exports = tableWriter;
@@ -88,6 +88,9 @@ function decryptConnection(connection) {
88
88
  }
89
89
 
90
90
  function pickSafeConnectionInfo(connection) {
91
+ if (process.env.LOG_CONNECTION_SENSITIVE_VALUES) {
92
+ return connection;
93
+ }
91
94
  return _.mapValues(connection, (v, k) => {
92
95
  if (k == 'engine' || k == 'port' || k == 'authType' || k == 'sshMode' || k == 'passwordMode') return v;
93
96
  if (v === null || v === true || v === false) return v;
@@ -0,0 +1,27 @@
1
+ const os = require('os');
2
+
3
+ const databaseConnections = require('../controllers/databaseConnections');
4
+ const serverConnections = require('../controllers/serverConnections');
5
+ const sessions = require('../controllers/sessions');
6
+ const runners = require('../controllers/runners');
7
+
8
+ async function getHealthStatus() {
9
+ const memory = process.memoryUsage();
10
+ const cpuUsage = process.cpuUsage();
11
+
12
+ return {
13
+ status: 'ok',
14
+ databaseConnectionCount: databaseConnections.opened.length,
15
+ serverConnectionCount: serverConnections.opened.length,
16
+ sessionCount: sessions.opened.length,
17
+ runProcessCount: runners.opened.length,
18
+ memory,
19
+ cpuUsage,
20
+ systemMemory: {
21
+ total: os.totalmem(),
22
+ free: os.freemem(),
23
+ },
24
+ };
25
+ }
26
+
27
+ module.exports = getHealthStatus;
@@ -0,0 +1,18 @@
1
+ const stream = require('stream');
2
+ const _ = require('lodash');
3
+
4
+ function streamPipeline(...processedStreams) {
5
+ const streams = _.flattenDeep(processedStreams);
6
+ return new Promise((resolve, reject) => {
7
+ // @ts-ignore
8
+ stream.pipeline(...streams, err => {
9
+ if (err) {
10
+ reject(err);
11
+ } else {
12
+ resolve();
13
+ }
14
+ });
15
+ });
16
+ }
17
+
18
+ module.exports = streamPipeline;
@@ -1,9 +0,0 @@
1
-
2
- // this file is generated automatically by script fillNativeModules.js, do not edit it manually
3
- const content = {};
4
-
5
- content['better-sqlite3'] = () => require('better-sqlite3');
6
- content['oracledb'] = () => require('oracledb');
7
-
8
-
9
- module.exports = content;
@@ -1,49 +0,0 @@
1
- const requireEngineDriver = require('../utility/requireEngineDriver');
2
- const { connectUtility } = require('../utility/connectUtility');
3
- const { getLogger } = require('dbgate-tools');
4
-
5
- const logger = getLogger('dumpDb');
6
-
7
- function doDump(dumper) {
8
- return new Promise((resolve, reject) => {
9
- dumper.once('end', () => {
10
- resolve(true);
11
- });
12
- dumper.once('error', err => {
13
- reject(err);
14
- });
15
- dumper.run();
16
- });
17
- }
18
-
19
- async function dumpDatabase({
20
- connection = undefined,
21
- systemConnection = undefined,
22
- driver = undefined,
23
- outputFile,
24
- databaseName,
25
- schemaName,
26
- }) {
27
- logger.info(`Dumping database`);
28
-
29
- if (!driver) driver = requireEngineDriver(connection);
30
-
31
- const dbhan = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
32
-
33
- try {
34
- logger.info(`Connected.`);
35
-
36
- const dumper = await driver.createBackupDumper(dbhan, {
37
- outputFile,
38
- databaseName,
39
- schemaName,
40
- });
41
- await doDump(dumper);
42
- } finally {
43
- if (!systemConnection) {
44
- await driver.close(dbhan);
45
- }
46
- }
47
- }
48
-
49
- module.exports = dumpDatabase;