mysql-memory-server 1.8.1 → 1.9.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/README.md +9 -49
- package/dist/src/cli.js +22 -17
- package/dist/src/constants.d.ts +80 -3
- package/dist/src/constants.js +100 -38
- package/dist/src/index.js +2 -57
- package/dist/src/libraries/Downloader.d.ts +0 -1
- package/dist/src/libraries/Downloader.js +132 -84
- package/dist/src/libraries/Executor.d.ts +2 -0
- package/dist/src/libraries/Executor.js +108 -88
- package/dist/src/libraries/FileLock.js +2 -2
- package/dist/src/libraries/LinuxOSRelease.d.ts +3 -0
- package/dist/src/libraries/LinuxOSRelease.js +18 -0
- package/dist/src/libraries/Version.d.ts +2 -5
- package/dist/src/libraries/Version.js +87 -22
- package/dist/types/index.d.ts +14 -18
- package/package.json +8 -8
|
@@ -37,7 +37,10 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
37
37
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
38
38
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
39
39
|
};
|
|
40
|
-
var
|
|
40
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
43
|
+
var _Executor_instances, _Executor_executeFile, _Executor_killProcess, _Executor_returnBinaryPathToDelete, _Executor_startMySQLProcess, _Executor_setupDataDirectories;
|
|
41
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
45
|
const child_process_1 = require("child_process");
|
|
43
46
|
const semver_1 = require("semver");
|
|
@@ -49,6 +52,8 @@ const path_1 = require("path");
|
|
|
49
52
|
const FileLock_1 = require("./FileLock");
|
|
50
53
|
const signal_exit_1 = require("signal-exit");
|
|
51
54
|
const crypto_1 = require("crypto");
|
|
55
|
+
const constants_1 = require("../constants");
|
|
56
|
+
const LinuxOSRelease_1 = __importDefault(require("./LinuxOSRelease"));
|
|
52
57
|
class Executor {
|
|
53
58
|
constructor(logger) {
|
|
54
59
|
_Executor_instances.add(this);
|
|
@@ -118,13 +123,13 @@ class Executor {
|
|
|
118
123
|
this.version = installedMySQLBinary.version;
|
|
119
124
|
this.versionInstalledOnSystem = installedMySQLBinary.installedOnSystem;
|
|
120
125
|
this.removeExitHandler = (0, signal_exit_1.onExit)(() => {
|
|
121
|
-
if (
|
|
126
|
+
if ((0, constants_1.getInternalEnvVariable)('cli') === 'true') {
|
|
122
127
|
console.log('\nShutting down the ephemeral MySQL database and cleaning all related files...');
|
|
123
128
|
}
|
|
124
129
|
this.DBDestroySignal.abort();
|
|
125
|
-
if (
|
|
130
|
+
if ((0, constants_1.getInternalEnvVariable)('deleteDBAfterStopped') === 'true') {
|
|
126
131
|
try {
|
|
127
|
-
fs.rmSync(
|
|
132
|
+
fs.rmSync(this.databasePath, { recursive: true, maxRetries: 50, force: true });
|
|
128
133
|
}
|
|
129
134
|
catch (e) {
|
|
130
135
|
this.logger.error('An error occurred while deleting database directory path:', e);
|
|
@@ -139,21 +144,22 @@ class Executor {
|
|
|
139
144
|
this.logger.error('An error occurred while deleting database binary:', e);
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
|
-
if (
|
|
147
|
+
if ((0, constants_1.getInternalEnvVariable)('cli') === 'true') {
|
|
143
148
|
console.log('Shutdown and cleanup is complete.');
|
|
144
149
|
}
|
|
145
150
|
});
|
|
146
151
|
let retries = 0;
|
|
147
|
-
|
|
152
|
+
this.databasePath = (0, path_1.normalize)(`${(0, constants_1.getInternalEnvVariable)('databaseDirectoryPath')}/${(0, crypto_1.randomUUID)().replaceAll("-", '')}`);
|
|
153
|
+
const datadir = (0, path_1.normalize)(`${this.databasePath}/data`);
|
|
148
154
|
do {
|
|
149
|
-
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, installedMySQLBinary
|
|
155
|
+
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, installedMySQLBinary, datadir, true);
|
|
150
156
|
this.logger.log('Setting up directories was successful');
|
|
151
157
|
const port = options.port || (0, Port_1.GenerateRandomPort)();
|
|
152
158
|
const mySQLXPort = options.xPort || (0, Port_1.GenerateRandomPort)();
|
|
153
159
|
this.logger.log('Using port:', port, 'and MySQLX port:', mySQLXPort, 'on retry:', retries);
|
|
154
160
|
try {
|
|
155
161
|
this.logger.log('Starting MySQL process');
|
|
156
|
-
const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir,
|
|
162
|
+
const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir, this.databasePath, installedMySQLBinary.path);
|
|
157
163
|
this.logger.log('Starting process was successful');
|
|
158
164
|
return resolved;
|
|
159
165
|
}
|
|
@@ -195,28 +201,6 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
|
|
|
195
201
|
killed = process.kill();
|
|
196
202
|
}
|
|
197
203
|
return killed;
|
|
198
|
-
}, _Executor_deleteDatabaseDirectory = async function _Executor_deleteDatabaseDirectory(path) {
|
|
199
|
-
let retries = 0;
|
|
200
|
-
//Maximum wait of 10 seconds | 500ms * 20 retries = 10,000ms = 10 seconds
|
|
201
|
-
const waitTime = 500;
|
|
202
|
-
const maxRetries = 20;
|
|
203
|
-
//Since the database processes are killed instantly (SIGKILL) sometimes the database file handles may still be open
|
|
204
|
-
//This would cause an EBUSY error. Retrying the deletions for 10 seconds should give the OS enough time to close
|
|
205
|
-
//the file handles.
|
|
206
|
-
while (retries <= maxRetries) {
|
|
207
|
-
try {
|
|
208
|
-
await fsPromises.rm(path, { recursive: true, force: true });
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
catch (e) {
|
|
212
|
-
if (retries === maxRetries) {
|
|
213
|
-
throw e;
|
|
214
|
-
}
|
|
215
|
-
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
216
|
-
retries++;
|
|
217
|
-
this.logger.log('DB data directory deletion failed. Now on retry', retries);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
204
|
}, _Executor_returnBinaryPathToDelete = function _Executor_returnBinaryPathToDelete(binaryFilepath, options) {
|
|
221
205
|
if (binaryFilepath.includes(os.tmpdir()) && !options.downloadBinaryOnce) {
|
|
222
206
|
const splitPath = binaryFilepath.split(os.platform() === 'win32' ? '\\' : '/');
|
|
@@ -234,12 +218,51 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
|
|
|
234
218
|
await fsPromises.rm(logFile, { force: true });
|
|
235
219
|
const socket = os.platform() === 'win32' ? `MySQL-${(0, crypto_1.randomUUID)()}` : `${dbPath}/m.sock`;
|
|
236
220
|
const xSocket = os.platform() === 'win32' ? `MySQLX-${(0, crypto_1.randomUUID)()}` : `${dbPath}/x.sock`;
|
|
237
|
-
const
|
|
221
|
+
const mysqlArguments = [
|
|
222
|
+
'--no-defaults',
|
|
223
|
+
'--mysqlx=FORCE',
|
|
224
|
+
`--port=${port}`,
|
|
225
|
+
`--datadir=${datadir}`,
|
|
226
|
+
`--mysqlx-port=${mySQLXPort}`,
|
|
227
|
+
`--mysqlx-socket=${xSocket}`,
|
|
228
|
+
`--socket=${socket}`,
|
|
229
|
+
`--general-log-file=${logFile}`,
|
|
230
|
+
'--general-log=1',
|
|
231
|
+
`--init-file=${dbPath}/init.sql`,
|
|
232
|
+
'--bind-address=127.0.0.1',
|
|
233
|
+
'--innodb-doublewrite=OFF',
|
|
234
|
+
`--log-error=${errorLogFile}`,
|
|
235
|
+
`--user=${os.userInfo().username}`
|
|
236
|
+
];
|
|
237
|
+
//<8.0.11 does not have MySQL X turned on by default so we will be installing the X Plugin in this if statement.
|
|
238
|
+
//MySQL 5.7.12 introduced the X plugin, but according to https://dev.mysql.com/doc/refman/5.7/en/document-store-setting-up.html, the database needs to be initialised with version 5.7.19.
|
|
239
|
+
//If the MySQL version is >=5.7.19 & <8.0.11 then install the X Plugin
|
|
240
|
+
if ((0, semver_1.lt)(this.version, '8.0.11') && (0, semver_1.gte)(this.version, '5.7.19')) {
|
|
241
|
+
const pluginExtension = os.platform() === 'win32' ? 'dll' : 'so';
|
|
242
|
+
let pluginPath;
|
|
243
|
+
const firstPath = (0, path_1.resolve)(`${binaryFilepath}/../../lib/plugin`);
|
|
244
|
+
const secondPath = '/usr/lib/mysql/plugin';
|
|
245
|
+
if (fs.existsSync(`${firstPath}/mysqlx.${pluginExtension}`)) {
|
|
246
|
+
pluginPath = firstPath;
|
|
247
|
+
}
|
|
248
|
+
else if (os.platform() === 'linux' && fs.existsSync(`${secondPath}/mysqlx.so`)) {
|
|
249
|
+
pluginPath = secondPath;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
throw 'Could not install MySQL X as the path to the plugin cannot be found.';
|
|
253
|
+
}
|
|
254
|
+
mysqlArguments.splice(1, 0, `--plugin-dir=${pluginPath}`, `--early-plugin-load=mysqlx=mysqlx.${pluginExtension};`);
|
|
255
|
+
}
|
|
256
|
+
const process = (0, child_process_1.spawn)(binaryFilepath, mysqlArguments, { signal: this.DBDestroySignal.signal, killSignal: 'SIGKILL' });
|
|
238
257
|
//resolveFunction is the function that will be called to resolve the promise that stops the database.
|
|
239
258
|
//If resolveFunction is not undefined, the database has received a kill signal and data cleanup procedures should run.
|
|
240
259
|
//Once ran, resolveFunction will be called.
|
|
241
260
|
let resolveFunction;
|
|
242
261
|
process.on('close', async (code, signal) => {
|
|
262
|
+
if (signal) {
|
|
263
|
+
this.logger.log('Exiting because of aborted signal.');
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
243
266
|
let errorLog;
|
|
244
267
|
try {
|
|
245
268
|
errorLog = await fsPromises.readFile(errorLogFile, { encoding: 'utf-8' });
|
|
@@ -247,13 +270,19 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
|
|
|
247
270
|
catch (e) {
|
|
248
271
|
errorLog = `ERROR WHILE READING LOG: ${e}`;
|
|
249
272
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
273
|
+
if (!this.killedFromPortIssue) {
|
|
274
|
+
//A check is done after the error log file says that the server is ready for connections.
|
|
275
|
+
//When MySQL X cannot bind to a port, the server still says it is ready for connections so killedFromPortIssue gets set to true
|
|
276
|
+
//This if statement will be ran if the server did not encounter a port issue or if the MySQL server could not bind to its port
|
|
277
|
+
this.killedFromPortIssue = errorLog.includes("Do you already have another mysqld server running");
|
|
278
|
+
this.logger.log('Did a port issue occur between server start and server close:', this.killedFromPortIssue);
|
|
279
|
+
}
|
|
280
|
+
if (this.killedFromPortIssue) {
|
|
254
281
|
this.logger.log('Error log when exiting for port in use error:', errorLog);
|
|
255
282
|
try {
|
|
256
|
-
|
|
283
|
+
this.logger.log('Deleting database path after port issue...');
|
|
284
|
+
await fsPromises.rm(dbPath, { recursive: true, force: true, maxRetries: 50, retryDelay: 100 });
|
|
285
|
+
this.logger.log('Successfully deleted database after port issue.');
|
|
257
286
|
}
|
|
258
287
|
catch (e) {
|
|
259
288
|
this.logger.error(e);
|
|
@@ -262,8 +291,8 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
|
|
|
262
291
|
return reject('Port is already in use');
|
|
263
292
|
}
|
|
264
293
|
try {
|
|
265
|
-
if (
|
|
266
|
-
await
|
|
294
|
+
if ((0, constants_1.getInternalEnvVariable)('deleteDBAfterStopped') === 'true') {
|
|
295
|
+
await fsPromises.rm(dbPath, { recursive: true, force: true, maxRetries: 50, retryDelay: 100 });
|
|
267
296
|
}
|
|
268
297
|
}
|
|
269
298
|
catch (e) {
|
|
@@ -310,18 +339,17 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
|
|
|
310
339
|
if (curr.dev !== 0) {
|
|
311
340
|
//File exists
|
|
312
341
|
const file = await fsPromises.readFile(errorLogFile, { encoding: 'utf8' });
|
|
313
|
-
if (file.includes(
|
|
314
|
-
//As stated in the MySQL X Plugin documentation at https://dev.mysql.com/doc/refman/8.4/en/x-plugin-options-system-variables.html#sysvar_mysqlx_bind_address
|
|
315
|
-
//when the MySQL X Plugin fails to bind to an address, it does not prevent the MySQL server startup because MySQL X is not a mandatory plugin.
|
|
316
|
-
//It doesn't seem like there is a way to prevent server startup when that happens. The workaround to that is to shutdown the MySQL server ourselves when the X plugin
|
|
317
|
-
//cannot bind to an address. If there is a way to prevent server startup when binding fails, this workaround can be removed.
|
|
318
|
-
const killed = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_killProcess).call(this, process);
|
|
319
|
-
if (!killed) {
|
|
320
|
-
reject('Failed to kill MySQL process to retry listening on a free port.');
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
else if (file.includes('ready for connections. Version:')) {
|
|
342
|
+
if (file.includes(': ready for connections') || file.includes('Server starts handling incoming connections')) {
|
|
324
343
|
fs.unwatchFile(errorLogFile);
|
|
344
|
+
this.killedFromPortIssue = file.includes("Do you already have another mysqld server running");
|
|
345
|
+
this.logger.log('Did a port issue occur after watching errorLogFile:', this.killedFromPortIssue);
|
|
346
|
+
if (this.killedFromPortIssue) {
|
|
347
|
+
const killed = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_killProcess).call(this, process);
|
|
348
|
+
if (!killed) {
|
|
349
|
+
return reject('Failed to kill process after port error.');
|
|
350
|
+
}
|
|
351
|
+
return; //Promise rejection will be handled in the process.on('close') section because this.killedFromPortIssue is being set to true
|
|
352
|
+
}
|
|
325
353
|
resolve({
|
|
326
354
|
port,
|
|
327
355
|
xPort: mySQLXPort,
|
|
@@ -339,7 +367,7 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
|
|
|
339
367
|
this.removeExitHandler();
|
|
340
368
|
const killed = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_killProcess).call(this, process);
|
|
341
369
|
if (!killed) {
|
|
342
|
-
reject();
|
|
370
|
+
reject('Failed to kill process.');
|
|
343
371
|
}
|
|
344
372
|
});
|
|
345
373
|
}
|
|
@@ -348,59 +376,53 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
|
|
|
348
376
|
}
|
|
349
377
|
});
|
|
350
378
|
});
|
|
351
|
-
}, _Executor_setupDataDirectories = async function _Executor_setupDataDirectories(options,
|
|
379
|
+
}, _Executor_setupDataDirectories = async function _Executor_setupDataDirectories(options, binary, datadir, retry) {
|
|
380
|
+
const binaryFilepath = binary.path;
|
|
352
381
|
this.logger.log('Created data directory for database at:', datadir);
|
|
353
382
|
await fsPromises.mkdir(datadir, { recursive: true });
|
|
354
|
-
|
|
355
|
-
if (
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
if (error) {
|
|
359
|
-
this.logger.error('An error occurred while initializing database with system-installed MySQL:', error);
|
|
360
|
-
throw 'An error occurred while initializing database with system-installed MySQL. Please check the console for more information.';
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
let result;
|
|
365
|
-
try {
|
|
366
|
-
result = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, `${binaryFilepath}`, [`--no-defaults`, `--datadir=${datadir}`, `--initialize-insecure`]);
|
|
367
|
-
}
|
|
368
|
-
catch (e) {
|
|
369
|
-
this.logger.error('Error occurred from executeFile:', e);
|
|
370
|
-
throw e;
|
|
371
|
-
}
|
|
372
|
-
stderr = result === null || result === void 0 ? void 0 : result.stderr;
|
|
383
|
+
const { error, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, binaryFilepath, ['--no-defaults', `--datadir=${datadir}`, '--initialize-insecure']);
|
|
384
|
+
if (binary.installedOnSystem && error) {
|
|
385
|
+
this.logger.error('An error occurred while initializing database with the system-installed MySQL binary:', error);
|
|
386
|
+
throw `An error occurred while initializing the MySQL database: ${error}`;
|
|
373
387
|
}
|
|
374
|
-
if (stderr && !stderr.includes('
|
|
388
|
+
if (stderr && !stderr.includes('is created with an empty password')) {
|
|
375
389
|
if (process.platform === 'win32' && stderr.includes('Command failed')) {
|
|
376
390
|
this.logger.error(stderr);
|
|
377
391
|
throw 'The mysqld command failed to run. A possible cause is that the Microsoft Visual C++ Redistributable Package is not installed. MySQL 5.7.40 and newer requires Microsoft Visual C++ Redistributable Package 2019 to be installed. Check the MySQL docs for Microsoft Visual C++ requirements for other MySQL versions. If you are sure you have this installed, check the error message in the console for more details.';
|
|
378
392
|
}
|
|
379
393
|
if (process.platform === 'linux' && stderr.includes('libaio.so')) {
|
|
380
|
-
if (binaryFilepath === 'mysqld') {
|
|
381
|
-
throw 'libaio could not be found while running system-installed MySQL. libaio must be installed on this system for MySQL to run. To learn more, please check out https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
382
|
-
}
|
|
383
394
|
if (retry === false) {
|
|
384
395
|
this.logger.error('An error occurred while initializing database:', stderr);
|
|
385
396
|
throw 'Tried to copy libaio into lib folder and MySQL is still failing to initialize. Please check the console for more information.';
|
|
386
397
|
}
|
|
387
|
-
if (
|
|
398
|
+
if (binary.installedOnSystem) {
|
|
399
|
+
throw 'libaio could not be found while running system-installed MySQL. libaio must be installed on this system for MySQL to run. To learn more, please check out https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
400
|
+
}
|
|
401
|
+
// If the below code is running, the version of MySQL that is trying to be executed was downloaded from the CDN by this package and libaio has not yet been attempted to be copied
|
|
402
|
+
if (LinuxOSRelease_1.default.NAME === 'Ubuntu' && LinuxOSRelease_1.default.VERSION_ID >= '24.04') {
|
|
388
403
|
const { error: lderror, stdout, stderr: ldstderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, 'ldconfig', ['-p']);
|
|
389
404
|
if (lderror || ldstderr) {
|
|
390
405
|
this.logger.error('The following libaio error occurred:', stderr);
|
|
391
406
|
this.logger.error('After the libaio error, an ldconfig error occurred:', lderror || ldstderr);
|
|
392
|
-
throw 'The ldconfig command failed to run. This command was ran to find
|
|
407
|
+
throw 'The ldconfig command failed to run. This command was ran to find libaio1t64 because libaio1t64 could not be found on the system. libaio1t64 is needed for MySQL to run. Do you have ldconfig and libaio1t64 installed? Learn more at https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
393
408
|
}
|
|
394
409
|
const libaioFound = stdout.split('\n').filter(lib => lib.includes('libaio.so.1t64'));
|
|
395
410
|
if (!libaioFound.length) {
|
|
396
411
|
this.logger.error('Error from launching MySQL:', stderr);
|
|
397
|
-
|
|
412
|
+
this.logger.error('Could not find libaio1t64 in this list:', libaioFound);
|
|
413
|
+
throw 'An error occurred while launching MySQL because libaio1t64 is not installed on your system. Please install libaio1t64 and then use mysql-memory-server again. To learn more, please check out https://dev.mysql.com/doc/refman/en/binary-installation.html. Check error in console for more information.';
|
|
398
414
|
}
|
|
399
415
|
const libaioEntry = libaioFound[0];
|
|
400
416
|
const libaioPathIndex = libaioEntry.indexOf('=>');
|
|
401
417
|
const libaioSymlinkPath = libaioEntry.slice(libaioPathIndex + 3);
|
|
402
418
|
const libaioPath = await fsPromises.realpath(libaioSymlinkPath);
|
|
403
|
-
|
|
419
|
+
let copyPath;
|
|
420
|
+
if ((0, semver_1.lt)(this.version, '8.0.18')) {
|
|
421
|
+
copyPath = (0, path_1.resolve)(`${binaryFilepath}/../../bin/libaio.so.1`);
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
copyPath = (0, path_1.resolve)(`${binaryFilepath}/../../lib/private/libaio.so.1`);
|
|
425
|
+
}
|
|
404
426
|
let lockRelease;
|
|
405
427
|
while (true) {
|
|
406
428
|
try {
|
|
@@ -454,30 +476,28 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
|
|
|
454
476
|
if (copyError) {
|
|
455
477
|
throw 'An error occurred while copying libaio1t64 to the MySQL lib folder. Please check the console for more details.';
|
|
456
478
|
}
|
|
457
|
-
//Retry setting up directory now that libaio has been copied
|
|
458
|
-
this.logger.log('Retrying directory setup');
|
|
459
|
-
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_deleteDatabaseDirectory).call(this, datadir);
|
|
460
|
-
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binaryFilepath, datadir, false);
|
|
461
|
-
return;
|
|
462
479
|
}
|
|
463
480
|
}
|
|
481
|
+
//Retry setting up directory now that libaio has been copied
|
|
482
|
+
this.logger.log('Retrying directory setup');
|
|
483
|
+
await fsPromises.rm(datadir, { recursive: true, force: true, maxRetries: 50, retryDelay: 100 });
|
|
484
|
+
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binary, datadir, false);
|
|
485
|
+
return;
|
|
464
486
|
}
|
|
465
|
-
|
|
466
|
-
throw 'Cannot recognize file structure for the MySQL binary folder. This was caused by not being able to find libaio. Try installing libaio. Learn more at https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
467
|
-
}
|
|
487
|
+
throw 'You do not have libaio1 installed. Please install the libaio1 package for the downloaded MySQL binary to run. Learn more here: https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
468
488
|
}
|
|
469
489
|
throw stderr;
|
|
470
490
|
}
|
|
471
491
|
this.logger.log('Creating init text');
|
|
472
492
|
let initText = `CREATE DATABASE ${options.dbName};`;
|
|
473
493
|
if (options.username !== 'root') {
|
|
474
|
-
initText += `\
|
|
494
|
+
initText += `\nCREATE USER '${options.username}'@'localhost';\nGRANT ALL ON *.* TO '${options.username}'@'localhost' WITH GRANT OPTION;`;
|
|
475
495
|
}
|
|
476
496
|
if (options.initSQLString.length > 0) {
|
|
477
497
|
initText += `\n${options.initSQLString}`;
|
|
478
498
|
}
|
|
479
499
|
this.logger.log('Writing init file');
|
|
480
|
-
await fsPromises.writeFile(`${
|
|
500
|
+
await fsPromises.writeFile(`${this.databasePath}/init.sql`, initText, { encoding: 'utf8' });
|
|
481
501
|
this.logger.log('Finished writing init file');
|
|
482
502
|
};
|
|
483
503
|
exports.default = Executor;
|
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.waitForLock = waitForLock;
|
|
7
7
|
exports.lockFile = lockFile;
|
|
8
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
-
const mtimeUpdateIntervalTime =
|
|
10
|
-
const mtimeLimit =
|
|
9
|
+
const mtimeUpdateIntervalTime = 2_000;
|
|
10
|
+
const mtimeLimit = 10_000;
|
|
11
11
|
async function waitForLock(path, options) {
|
|
12
12
|
const lockPath = `${path}.lock`;
|
|
13
13
|
let retries = 0;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const releaseDetails = {};
|
|
8
|
+
if (process.platform === 'linux') {
|
|
9
|
+
const file = fs_1.default.readFileSync('/etc/os-release', 'utf8');
|
|
10
|
+
const entries = file.split('\n');
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
const [key, value] = entry.split('=');
|
|
13
|
+
if (typeof key === 'string' && typeof value === 'string') {
|
|
14
|
+
releaseDetails[key] = value.replaceAll('"', '');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.default = releaseDetails;
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default function getBinaryURL(
|
|
3
|
-
url: string;
|
|
4
|
-
version: string;
|
|
5
|
-
};
|
|
1
|
+
import { BinaryInfo } from "../../types";
|
|
2
|
+
export default function getBinaryURL(versionToGet: string, currentArch: string): BinaryInfo;
|
|
@@ -32,34 +32,99 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
exports.default = getBinaryURL;
|
|
37
40
|
const os = __importStar(require("os"));
|
|
38
41
|
const semver_1 = require("semver");
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
const constants_1 = require("../constants");
|
|
43
|
+
const LinuxOSRelease_1 = __importDefault(require("./LinuxOSRelease"));
|
|
44
|
+
function getBinaryURL(versionToGet = "x", currentArch) {
|
|
45
|
+
let selectedVersions = constants_1.DOWNLOADABLE_MYSQL_VERSIONS.filter(version => (0, semver_1.satisfies)(version, versionToGet));
|
|
46
|
+
if (selectedVersions.length === 0) {
|
|
47
|
+
throw `mysql-memory-server does not support downloading a version of MySQL that fits the following version requirement: ${versionToGet}. This package only supports downloads of MySQL for MySQL >= ${constants_1.DOWNLOADABLE_MYSQL_VERSIONS[0]} <= ${constants_1.DOWNLOADABLE_MYSQL_VERSIONS.at(-1)}. Please check for typos, choose a different version of MySQL to use, or make an issue or pull request on GitHub if you belive this is a bug.`;
|
|
48
|
+
}
|
|
49
|
+
const currentOS = os.platform();
|
|
50
|
+
const OSVersionSupport = constants_1.MYSQL_MIN_OS_SUPPORT[currentOS];
|
|
51
|
+
if (!OSVersionSupport)
|
|
52
|
+
throw `MySQL and/or mysql-memory-server does not support your operating system. Please make sure you are running the latest version of mysql-memory-server or try running on a different operating system or report an issue on GitHub if you believe this is a bug.`;
|
|
53
|
+
const OSSupportVersionRanges = Object.keys(OSVersionSupport);
|
|
54
|
+
selectedVersions = selectedVersions.filter(possibleVersion => {
|
|
55
|
+
const OSKey = OSSupportVersionRanges.find(item => (0, semver_1.satisfies)(possibleVersion, item));
|
|
56
|
+
return !!OSKey;
|
|
57
|
+
});
|
|
58
|
+
if (selectedVersions.length === 0) {
|
|
59
|
+
throw `No version of MySQL could be found that supports your operating system and fits the following version requirement: ${versionToGet}. Please check for typos, choose a different version of MySQL to run, or if you think this is a bug, please report this on GitHub.`;
|
|
60
|
+
}
|
|
61
|
+
const archSupport = constants_1.MYSQL_ARCH_SUPPORT[currentOS][currentArch];
|
|
62
|
+
if (!archSupport) {
|
|
63
|
+
if (currentOS === 'win32' && currentArch === 'arm64')
|
|
64
|
+
throw 'mysql-memory-server has detected you are running Windows on ARM. MySQL does not support Windows on ARM. To get this package working, please try setting the "arch" option to "x64".';
|
|
65
|
+
throw `MySQL and/or mysql-memory-server does not support the CPU architecture you want to use (${currentArch}). Please make sure you are using the latest version of mysql-memory-server or try using a different architecture, or if you believe this is a bug, please report this on GitHub.`;
|
|
66
|
+
}
|
|
67
|
+
selectedVersions = selectedVersions.filter(possibleVersion => (0, semver_1.satisfies)(possibleVersion, archSupport));
|
|
68
|
+
if (selectedVersions.length === 0) {
|
|
69
|
+
throw `No version of MySQL could be found that supports the CPU architecture ${currentArch === os.arch() ? 'for your system' : 'you have chosen'} (${currentArch}). Please try choosing a different version of MySQL, or if you believe this is a bug, please report this on GitHub.`;
|
|
70
|
+
}
|
|
71
|
+
const versionsBeforeOSVersionCheck = selectedVersions.slice();
|
|
72
|
+
const coercedOSRelease = (0, semver_1.coerce)(os.release());
|
|
73
|
+
selectedVersions = selectedVersions.filter(possibleVersion => {
|
|
74
|
+
const OSVersionKey = OSSupportVersionRanges.find(item => (0, semver_1.satisfies)(possibleVersion, item));
|
|
75
|
+
return !(0, semver_1.lt)(coercedOSRelease, OSVersionSupport[OSVersionKey]);
|
|
52
76
|
});
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
if (selectedVersions.length === 0) {
|
|
78
|
+
const versionKeys = new Set();
|
|
79
|
+
for (const v of versionsBeforeOSVersionCheck) {
|
|
80
|
+
versionKeys.add(OSSupportVersionRanges.find(item => (0, semver_1.satisfies)(v, item)));
|
|
81
|
+
}
|
|
82
|
+
const minVersions = Array.from(versionKeys).map(v => OSVersionSupport[v]);
|
|
83
|
+
//Sorts versions in ascending order
|
|
84
|
+
minVersions.sort((a, b) => a < b ? -1 : 1);
|
|
85
|
+
const minVersion = minVersions[0];
|
|
86
|
+
throw `Your operating system is too out of date to run a version of MySQL that fits the following requirement: ${versionToGet}. The oldest version for your operating system that you would need to get a version that satisfies the version requirement is ${minVersion} but your current operating system is ${coercedOSRelease.version}. Please try changing your MySQL version requirement, updating your OS to a newer version, or if you believe this is a bug, please report this on GitHub.`;
|
|
87
|
+
}
|
|
88
|
+
if (process.platform === 'linux' && LinuxOSRelease_1.default.NAME === 'Ubuntu' && LinuxOSRelease_1.default.VERSION_ID >= '24.04') {
|
|
89
|
+
//Since Ubuntu >= 24.04 uses libaio1t64 instead of libaio, this package has to copy libaio1t64 into a folder that MySQL looks in for dynamically linked libraries with the filename "libaio.so.1".
|
|
90
|
+
//I have not been able to find a suitable folder for libaio1t64 to be copied into for MySQL < 8.0.4, so here we are filtering all versions lower than 8.0.4 since they fail to launch in Ubuntu 24.04.
|
|
91
|
+
//If there is a suitable filepath for libaio1t64 to be copied into for MySQL < 8.0.4 then this check can be removed and these older MySQL versions can run on Ubuntu.
|
|
92
|
+
//Pull requests are welcome for adding >= Ubuntu 24.04 support for MySQL < 8.0.4.
|
|
93
|
+
//A way to get MySQL running on Ubuntu >= 24.04 is to symlink libaio1t64 to the location libaio would be. It is not suitable for this package to be doing that automatically, so instead this package has been copying libaio1t64 into the MySQL binary folder.
|
|
94
|
+
selectedVersions = selectedVersions.filter(v => !(0, semver_1.lt)(v, '8.0.4'));
|
|
95
|
+
}
|
|
96
|
+
if (selectedVersions.length === 0) {
|
|
97
|
+
throw `You are running a version of Ubuntu that is too modern to run any MySQL versions with this package that match the following version requirement: ${versionToGet}. Please choose a newer version of MySQL to use, or if you believe this is a bug please report this on GitHub.`;
|
|
98
|
+
}
|
|
58
99
|
//Sorts versions in descending order
|
|
59
|
-
|
|
60
|
-
const
|
|
100
|
+
selectedVersions.sort((a, b) => a < b ? 1 : -1);
|
|
101
|
+
const selectedVersion = selectedVersions[0];
|
|
102
|
+
const isRC = (0, semver_1.satisfies)(selectedVersion, constants_1.RC_MYSQL_VERSIONS);
|
|
103
|
+
const isDMR = (0, semver_1.satisfies)(selectedVersion, constants_1.DMR_MYSQL_VERSIONS);
|
|
104
|
+
let fileLocation = '';
|
|
105
|
+
if (currentOS === 'win32') {
|
|
106
|
+
fileLocation = `${(0, semver_1.major)(selectedVersion)}.${(0, semver_1.minor)(selectedVersion)}/mysql-${selectedVersion}${isRC ? '-rc' : isDMR ? '-dmr' : ''}-winx64.zip`;
|
|
107
|
+
}
|
|
108
|
+
else if (currentOS === 'darwin') {
|
|
109
|
+
const MySQLmacOSVersionNameKeys = Object.keys(constants_1.MYSQL_MACOS_VERSIONS_IN_FILENAME);
|
|
110
|
+
const macOSVersionNameKey = MySQLmacOSVersionNameKeys.find(range => (0, semver_1.satisfies)(selectedVersion, range));
|
|
111
|
+
fileLocation = `${(0, semver_1.major)(selectedVersion)}.${(0, semver_1.minor)(selectedVersion)}/mysql-${selectedVersion}${isRC ? '-rc' : isDMR ? '-dmr' : ''}-${constants_1.MYSQL_MACOS_VERSIONS_IN_FILENAME[macOSVersionNameKey]}-${currentArch === 'x64' ? 'x86_64' : 'arm64'}.tar.gz`;
|
|
112
|
+
}
|
|
113
|
+
else if (currentOS === 'linux') {
|
|
114
|
+
const glibcVersionKeys = Object.keys(constants_1.MYSQL_LINUX_GLIBC_VERSIONS);
|
|
115
|
+
const glibcVersionKey = glibcVersionKeys.find(range => (0, semver_1.satisfies)(selectedVersion, range));
|
|
116
|
+
const glibcVersion = constants_1.MYSQL_LINUX_GLIBC_VERSIONS[glibcVersionKey];
|
|
117
|
+
const minimalInstallAvailableKeys = Object.keys(constants_1.MYSQL_LINUX_MINIMAL_INSTALL_AVAILABLE);
|
|
118
|
+
const minimalInstallAvailableKey = minimalInstallAvailableKeys.find(range => (0, semver_1.satisfies)(selectedVersion, range));
|
|
119
|
+
const minimalInstallAvailable = constants_1.MYSQL_LINUX_MINIMAL_INSTALL_AVAILABLE[minimalInstallAvailableKey];
|
|
120
|
+
const fileExtensionObject = constants_1.MYSQL_LINUX_FILE_EXTENSIONS[currentArch];
|
|
121
|
+
const fileExtensionKeys = Object.keys(fileExtensionObject);
|
|
122
|
+
const fileExtensionKey = fileExtensionKeys.find(range => (0, semver_1.satisfies)(selectedVersion, range));
|
|
123
|
+
const fileExtension = constants_1.MYSQL_LINUX_FILE_EXTENSIONS[currentArch][fileExtensionKey];
|
|
124
|
+
fileLocation = `${(0, semver_1.major)(selectedVersion)}.${(0, semver_1.minor)(selectedVersion)}/mysql-${selectedVersion}${isRC ? '-rc' : isDMR ? '-dmr' : ''}-linux-${minimalInstallAvailable !== 'no-glibc-tag' ? `glibc${glibcVersion}-` : ''}${currentArch === 'x64' ? 'x86_64' : 'aarch64'}${minimalInstallAvailable !== 'no' && (process.arch !== 'arm64' ? true : (0, semver_1.satisfies)(selectedVersion, constants_1.MYSQL_LINUX_MINIMAL_INSTALL_AVAILABLE_ARM64)) ? `-minimal${(0, semver_1.satisfies)(selectedVersion, constants_1.MYSQL_LINUX_MINIMAL_REBUILD_VERSIONS) ? '-rebuild' : ''}` : ''}.tar.${fileExtension}`;
|
|
125
|
+
}
|
|
61
126
|
return {
|
|
62
|
-
|
|
63
|
-
|
|
127
|
+
version: selectedVersion,
|
|
128
|
+
url: constants_1.archiveBaseURL + fileLocation
|
|
64
129
|
};
|
|
65
130
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -15,10 +15,6 @@ export type ServerOptions = {
|
|
|
15
15
|
downloadRetries?: number | undefined;
|
|
16
16
|
initSQLString?: string | undefined;
|
|
17
17
|
arch?: "arm64" | "x64" | undefined;
|
|
18
|
-
_DO_NOT_USE_deleteDBAfterStopped?: boolean | undefined;
|
|
19
|
-
_DO_NOT_USE_dbPath?: string | undefined;
|
|
20
|
-
_DO_NOT_USE_binaryDirectoryPath?: string | undefined;
|
|
21
|
-
_DO_NOT_USE_cli?: boolean | undefined;
|
|
22
18
|
};
|
|
23
19
|
export type InternalServerOptions = {
|
|
24
20
|
version?: string | undefined;
|
|
@@ -35,13 +31,6 @@ export type InternalServerOptions = {
|
|
|
35
31
|
downloadRetries: number;
|
|
36
32
|
initSQLString: string;
|
|
37
33
|
arch: string;
|
|
38
|
-
_DO_NOT_USE_deleteDBAfterStopped: boolean;
|
|
39
|
-
_DO_NOT_USE_dbPath: string;
|
|
40
|
-
_DO_NOT_USE_binaryDirectoryPath: string;
|
|
41
|
-
_DO_NOT_USE_cli: boolean;
|
|
42
|
-
};
|
|
43
|
-
export type ExecutorOptions = {
|
|
44
|
-
logLevel: LOG_LEVEL;
|
|
45
34
|
};
|
|
46
35
|
export type ExecuteFileReturn = {
|
|
47
36
|
error: ExecFileException | null;
|
|
@@ -61,13 +50,6 @@ export type MySQLDB = {
|
|
|
61
50
|
};
|
|
62
51
|
stop: () => Promise<void>;
|
|
63
52
|
};
|
|
64
|
-
export type MySQLVersion = {
|
|
65
|
-
version: string;
|
|
66
|
-
arch: string;
|
|
67
|
-
os: string;
|
|
68
|
-
osKernelVersionsSupported: string;
|
|
69
|
-
url: string;
|
|
70
|
-
};
|
|
71
53
|
export type DownloadedMySQLVersion = {
|
|
72
54
|
version: string;
|
|
73
55
|
path: string;
|
|
@@ -84,3 +66,17 @@ export type OptionTypeChecks = {
|
|
|
84
66
|
definedType: "string" | "boolean" | "number";
|
|
85
67
|
};
|
|
86
68
|
};
|
|
69
|
+
export type LinuxEtcOSRelease = {
|
|
70
|
+
PRETTY_NAME?: string;
|
|
71
|
+
NAME?: string;
|
|
72
|
+
VERSION_ID?: string;
|
|
73
|
+
VERSION?: string;
|
|
74
|
+
VERSION_CODENAME?: string;
|
|
75
|
+
ID?: string;
|
|
76
|
+
ID_LIKE?: string;
|
|
77
|
+
UBUNTU_CODENAME?: string;
|
|
78
|
+
};
|
|
79
|
+
export type JSRuntimeVersion = {
|
|
80
|
+
runtimeName: string;
|
|
81
|
+
runtimeVersion: string;
|
|
82
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mysql-memory-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Spin up an ephemeral MySQL database from your JavaScript code",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"memory database"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"test": "jest --
|
|
20
|
-
"test:ci": "jest --
|
|
21
|
-
"
|
|
19
|
+
"test": "jest --verbose --colors",
|
|
20
|
+
"test:ci": "jest --setupFilesAfterEnv ./ciSetup.js --verbose --colors --runTestsByPath tests/concurrency.test.ts",
|
|
21
|
+
"os-compat:ci": "jest --setupFilesAfterEnv ./ciSetup.js --verbose --colors --runTestsByPath tests/versions.test.ts"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=16.6.0",
|
|
@@ -31,16 +31,16 @@
|
|
|
31
31
|
"@babel/preset-env": "^7.25.9",
|
|
32
32
|
"@babel/preset-typescript": "^7.25.9",
|
|
33
33
|
"@types/adm-zip": "^0.5.5",
|
|
34
|
-
"@types/node": "^22.
|
|
34
|
+
"@types/node": "^22.10.6",
|
|
35
35
|
"@types/semver": "^7.5.8",
|
|
36
36
|
"babel-jest": "^29.7.0",
|
|
37
37
|
"jest": "^29.7.0",
|
|
38
|
-
"mysql2": "^3.
|
|
39
|
-
"typescript": "^5.7.
|
|
38
|
+
"mysql2": "^3.12.0",
|
|
39
|
+
"typescript": "^5.7.3"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"adm-zip": "^0.5.16",
|
|
43
|
-
"semver": "^7.
|
|
43
|
+
"semver": "^7.7.1",
|
|
44
44
|
"signal-exit": "^4.1.0"
|
|
45
45
|
},
|
|
46
46
|
"repository": {
|