mysql-memory-server 1.8.2 → 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.
@@ -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 _Executor_instances, _Executor_executeFile, _Executor_killProcess, _Executor_deleteDatabaseDirectory, _Executor_returnBinaryPathToDelete, _Executor_startMySQLProcess, _Executor_setupDataDirectories;
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");
@@ -50,6 +53,7 @@ const FileLock_1 = require("./FileLock");
50
53
  const signal_exit_1 = require("signal-exit");
51
54
  const crypto_1 = require("crypto");
52
55
  const constants_1 = require("../constants");
56
+ const LinuxOSRelease_1 = __importDefault(require("./LinuxOSRelease"));
53
57
  class Executor {
54
58
  constructor(logger) {
55
59
  _Executor_instances.add(this);
@@ -125,7 +129,7 @@ class Executor {
125
129
  this.DBDestroySignal.abort();
126
130
  if ((0, constants_1.getInternalEnvVariable)('deleteDBAfterStopped') === 'true') {
127
131
  try {
128
- fs.rmSync((0, constants_1.getInternalEnvVariable)('dbPath'), { recursive: true, maxRetries: 50, force: true });
132
+ fs.rmSync(this.databasePath, { recursive: true, maxRetries: 50, force: true });
129
133
  }
130
134
  catch (e) {
131
135
  this.logger.error('An error occurred while deleting database directory path:', e);
@@ -145,16 +149,17 @@ class Executor {
145
149
  }
146
150
  });
147
151
  let retries = 0;
148
- const datadir = (0, path_1.normalize)(`${(0, constants_1.getInternalEnvVariable)('dbPath')}/data`);
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`);
149
154
  do {
150
- await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, installedMySQLBinary.path, datadir, true);
155
+ await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, installedMySQLBinary, datadir, true);
151
156
  this.logger.log('Setting up directories was successful');
152
157
  const port = options.port || (0, Port_1.GenerateRandomPort)();
153
158
  const mySQLXPort = options.xPort || (0, Port_1.GenerateRandomPort)();
154
159
  this.logger.log('Using port:', port, 'and MySQLX port:', mySQLXPort, 'on retry:', retries);
155
160
  try {
156
161
  this.logger.log('Starting MySQL process');
157
- const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir, (0, constants_1.getInternalEnvVariable)('dbPath'), installedMySQLBinary.path);
162
+ const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir, this.databasePath, installedMySQLBinary.path);
158
163
  this.logger.log('Starting process was successful');
159
164
  return resolved;
160
165
  }
@@ -196,28 +201,6 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
196
201
  killed = process.kill();
197
202
  }
198
203
  return killed;
199
- }, _Executor_deleteDatabaseDirectory = async function _Executor_deleteDatabaseDirectory(path) {
200
- let retries = 0;
201
- //Maximum wait of 10 seconds | 500ms * 20 retries = 10,000ms = 10 seconds
202
- const waitTime = 500;
203
- const maxRetries = 20;
204
- //Since the database processes are killed instantly (SIGKILL) sometimes the database file handles may still be open
205
- //This would cause an EBUSY error. Retrying the deletions for 10 seconds should give the OS enough time to close
206
- //the file handles.
207
- while (retries <= maxRetries) {
208
- try {
209
- await fsPromises.rm(path, { recursive: true, force: true });
210
- return;
211
- }
212
- catch (e) {
213
- if (retries === maxRetries) {
214
- throw e;
215
- }
216
- await new Promise(resolve => setTimeout(resolve, waitTime));
217
- retries++;
218
- this.logger.log('DB data directory deletion failed. Now on retry', retries);
219
- }
220
- }
221
204
  }, _Executor_returnBinaryPathToDelete = function _Executor_returnBinaryPathToDelete(binaryFilepath, options) {
222
205
  if (binaryFilepath.includes(os.tmpdir()) && !options.downloadBinaryOnce) {
223
206
  const splitPath = binaryFilepath.split(os.platform() === 'win32' ? '\\' : '/');
@@ -235,12 +218,51 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
235
218
  await fsPromises.rm(logFile, { force: true });
236
219
  const socket = os.platform() === 'win32' ? `MySQL-${(0, crypto_1.randomUUID)()}` : `${dbPath}/m.sock`;
237
220
  const xSocket = os.platform() === 'win32' ? `MySQLX-${(0, crypto_1.randomUUID)()}` : `${dbPath}/x.sock`;
238
- const process = (0, child_process_1.spawn)(binaryFilepath, ['--no-defaults', `--port=${port}`, `--datadir=${datadir}`, `--mysqlx-port=${mySQLXPort}`, `--mysqlx-socket=${xSocket}`, `--socket=${socket}`, `--general-log-file=${logFile}`, '--general-log=1', `--init-file=${dbPath}/init.sql`, '--bind-address=127.0.0.1', '--innodb-doublewrite=OFF', '--mysqlx=FORCE', `--log-error=${errorLogFile}`, `--user=${os.userInfo().username}`], { signal: this.DBDestroySignal.signal, killSignal: 'SIGKILL' });
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' });
239
257
  //resolveFunction is the function that will be called to resolve the promise that stops the database.
240
258
  //If resolveFunction is not undefined, the database has received a kill signal and data cleanup procedures should run.
241
259
  //Once ran, resolveFunction will be called.
242
260
  let resolveFunction;
243
261
  process.on('close', async (code, signal) => {
262
+ if (signal) {
263
+ this.logger.log('Exiting because of aborted signal.');
264
+ return;
265
+ }
244
266
  let errorLog;
245
267
  try {
246
268
  errorLog = await fsPromises.readFile(errorLogFile, { encoding: 'utf-8' });
@@ -248,13 +270,19 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
248
270
  catch (e) {
249
271
  errorLog = `ERROR WHILE READING LOG: ${e}`;
250
272
  }
251
- const portIssue = errorLog.includes("Do you already have another mysqld server running");
252
- const xPortIssue = errorLog.includes("X Plugin can't bind to it");
253
- this.logger.log('Exiting because of a port issue:', portIssue, '. MySQL X Plugin failed to bind:', xPortIssue);
254
- if (portIssue || xPortIssue) {
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) {
255
281
  this.logger.log('Error log when exiting for port in use error:', errorLog);
256
282
  try {
257
- await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_deleteDatabaseDirectory).call(this, (0, constants_1.getInternalEnvVariable)('dbPath'));
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.');
258
286
  }
259
287
  catch (e) {
260
288
  this.logger.error(e);
@@ -264,7 +292,7 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
264
292
  }
265
293
  try {
266
294
  if ((0, constants_1.getInternalEnvVariable)('deleteDBAfterStopped') === 'true') {
267
- await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_deleteDatabaseDirectory).call(this, dbPath);
295
+ await fsPromises.rm(dbPath, { recursive: true, force: true, maxRetries: 50, retryDelay: 100 });
268
296
  }
269
297
  }
270
298
  catch (e) {
@@ -311,18 +339,17 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
311
339
  if (curr.dev !== 0) {
312
340
  //File exists
313
341
  const file = await fsPromises.readFile(errorLogFile, { encoding: 'utf8' });
314
- if (file.includes("X Plugin can't bind to it")) {
315
- //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
316
- //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.
317
- //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
318
- //cannot bind to an address. If there is a way to prevent server startup when binding fails, this workaround can be removed.
319
- const killed = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_killProcess).call(this, process);
320
- if (!killed) {
321
- reject('Failed to kill MySQL process to retry listening on a free port.');
322
- }
323
- }
324
- else if (file.includes('ready for connections. Version:')) {
342
+ if (file.includes(': ready for connections') || file.includes('Server starts handling incoming connections')) {
325
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
+ }
326
353
  resolve({
327
354
  port,
328
355
  xPort: mySQLXPort,
@@ -340,7 +367,7 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
340
367
  this.removeExitHandler();
341
368
  const killed = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_killProcess).call(this, process);
342
369
  if (!killed) {
343
- reject();
370
+ reject('Failed to kill process.');
344
371
  }
345
372
  });
346
373
  }
@@ -349,59 +376,53 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
349
376
  }
350
377
  });
351
378
  });
352
- }, _Executor_setupDataDirectories = async function _Executor_setupDataDirectories(options, binaryFilepath, datadir, retry) {
379
+ }, _Executor_setupDataDirectories = async function _Executor_setupDataDirectories(options, binary, datadir, retry) {
380
+ const binaryFilepath = binary.path;
353
381
  this.logger.log('Created data directory for database at:', datadir);
354
382
  await fsPromises.mkdir(datadir, { recursive: true });
355
- let stderr;
356
- if (binaryFilepath === 'mysqld') {
357
- const { error, stderr: output } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, 'mysqld', ['--no-defaults', `--datadir=${datadir}`, '--initialize-insecure']);
358
- stderr = output;
359
- if (error) {
360
- this.logger.error('An error occurred while initializing database with system-installed MySQL:', error);
361
- throw 'An error occurred while initializing database with system-installed MySQL. Please check the console for more information.';
362
- }
363
- }
364
- else {
365
- let result;
366
- try {
367
- result = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, `${binaryFilepath}`, [`--no-defaults`, `--datadir=${datadir}`, `--initialize-insecure`]);
368
- }
369
- catch (e) {
370
- this.logger.error('Error occurred from executeFile:', e);
371
- throw e;
372
- }
373
- 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}`;
374
387
  }
375
- if (stderr && !stderr.includes('InnoDB initialization has ended')) {
388
+ if (stderr && !stderr.includes('is created with an empty password')) {
376
389
  if (process.platform === 'win32' && stderr.includes('Command failed')) {
377
390
  this.logger.error(stderr);
378
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.';
379
392
  }
380
393
  if (process.platform === 'linux' && stderr.includes('libaio.so')) {
381
- if (binaryFilepath === 'mysqld') {
382
- 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';
383
- }
384
394
  if (retry === false) {
385
395
  this.logger.error('An error occurred while initializing database:', stderr);
386
396
  throw 'Tried to copy libaio into lib folder and MySQL is still failing to initialize. Please check the console for more information.';
387
397
  }
388
- if (binaryFilepath.slice(-16) === 'mysql/bin/mysqld') {
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') {
389
403
  const { error: lderror, stdout, stderr: ldstderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, 'ldconfig', ['-p']);
390
404
  if (lderror || ldstderr) {
391
405
  this.logger.error('The following libaio error occurred:', stderr);
392
406
  this.logger.error('After the libaio error, an ldconfig error occurred:', lderror || ldstderr);
393
- throw 'The ldconfig command failed to run. This command was ran to find libaio because libaio could not be found on the system. libaio is needed for MySQL to run. Do you have ldconfig and libaio installed? Learn more about libaio at Learn more at https://dev.mysql.com/doc/refman/en/binary-installation.html';
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';
394
408
  }
395
409
  const libaioFound = stdout.split('\n').filter(lib => lib.includes('libaio.so.1t64'));
396
410
  if (!libaioFound.length) {
397
411
  this.logger.error('Error from launching MySQL:', stderr);
398
- throw 'An error occurred while launching MySQL. The most likely cause is that libaio1 and libaio1t64 could not be found. Either libaio1 or libaio1t64 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. Check error in console for more information.';
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.';
399
414
  }
400
415
  const libaioEntry = libaioFound[0];
401
416
  const libaioPathIndex = libaioEntry.indexOf('=>');
402
417
  const libaioSymlinkPath = libaioEntry.slice(libaioPathIndex + 3);
403
418
  const libaioPath = await fsPromises.realpath(libaioSymlinkPath);
404
- const copyPath = (0, path_1.resolve)(`${binaryFilepath}/../../lib/private/libaio.so.1`);
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
+ }
405
426
  let lockRelease;
406
427
  while (true) {
407
428
  try {
@@ -455,30 +476,28 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
455
476
  if (copyError) {
456
477
  throw 'An error occurred while copying libaio1t64 to the MySQL lib folder. Please check the console for more details.';
457
478
  }
458
- //Retry setting up directory now that libaio has been copied
459
- this.logger.log('Retrying directory setup');
460
- await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_deleteDatabaseDirectory).call(this, datadir);
461
- await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binaryFilepath, datadir, false);
462
- return;
463
479
  }
464
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;
465
486
  }
466
- else {
467
- 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';
468
- }
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';
469
488
  }
470
489
  throw stderr;
471
490
  }
472
491
  this.logger.log('Creating init text');
473
492
  let initText = `CREATE DATABASE ${options.dbName};`;
474
493
  if (options.username !== 'root') {
475
- initText += `\nRENAME USER 'root'@'localhost' TO '${options.username}'@'localhost';`;
494
+ initText += `\nCREATE USER '${options.username}'@'localhost';\nGRANT ALL ON *.* TO '${options.username}'@'localhost' WITH GRANT OPTION;`;
476
495
  }
477
496
  if (options.initSQLString.length > 0) {
478
497
  initText += `\n${options.initSQLString}`;
479
498
  }
480
499
  this.logger.log('Writing init file');
481
- await fsPromises.writeFile(`${(0, constants_1.getInternalEnvVariable)('dbPath')}/init.sql`, initText, { encoding: 'utf8' });
500
+ await fsPromises.writeFile(`${this.databasePath}/init.sql`, initText, { encoding: 'utf8' });
482
501
  this.logger.log('Finished writing init file');
483
502
  };
484
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 = 2000;
10
- const mtimeLimit = 10000;
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,3 @@
1
+ import { LinuxEtcOSRelease } from '../../types';
2
+ declare const _default: LinuxEtcOSRelease;
3
+ export default _default;
@@ -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 { InternalServerOptions, MySQLVersion } from "../../types";
2
- export default function getBinaryURL(versions: MySQLVersion[], versionToGet: string, options: InternalServerOptions): {
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
- function getBinaryURL(versions, versionToGet = "x", options) {
40
- let availableVersions = versions;
41
- availableVersions = availableVersions.filter(v => v.arch === options.arch);
42
- if (availableVersions.length === 0)
43
- throw `No MySQL binary could be found for your CPU architecture: ${options.arch}. ${process.platform === 'win32' && process.arch === 'arm64' ? 'This package 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".' : ''}`;
44
- availableVersions = availableVersions.filter(v => v.os === process.platform);
45
- if (availableVersions.length === 0)
46
- throw `No MySQL binary could be found for your OS: ${process.platform}`;
47
- availableVersions = availableVersions.filter(v => {
48
- const release = (0, semver_1.coerce)(os.release());
49
- if (!release)
50
- return false;
51
- return (0, semver_1.satisfies)(release.version, v.osKernelVersionsSupported);
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 (availableVersions.length === 0)
54
- throw `No MySQL binary could be found that supports your OS version: ${os.release()} | ${os.version()}`;
55
- const wantedVersions = availableVersions.filter(v => (0, semver_1.satisfies)(v.version, versionToGet));
56
- if (wantedVersions.length === 0)
57
- throw `No MySQL binary could be found that meets your version requirement: ${versionToGet} for OS ${process.platform} version ${os.release()} on arch ${process.arch}. The available versions for download are: ${availableVersions.map(v => v.version)}`;
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
- wantedVersions.sort((a, b) => a.version < b.version ? 1 : a.version === b.version ? 0 : -1);
60
- const v = wantedVersions[0];
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
- url: v.url,
63
- version: v.version
127
+ version: selectedVersion,
128
+ url: constants_1.archiveBaseURL + fileLocation
64
129
  };
65
130
  }
@@ -32,9 +32,6 @@ export type InternalServerOptions = {
32
32
  initSQLString: string;
33
33
  arch: string;
34
34
  };
35
- export type ExecutorOptions = {
36
- logLevel: LOG_LEVEL;
37
- };
38
35
  export type ExecuteFileReturn = {
39
36
  error: ExecFileException | null;
40
37
  stdout: string;
@@ -53,13 +50,6 @@ export type MySQLDB = {
53
50
  };
54
51
  stop: () => Promise<void>;
55
52
  };
56
- export type MySQLVersion = {
57
- version: string;
58
- arch: string;
59
- os: string;
60
- osKernelVersionsSupported: string;
61
- url: string;
62
- };
63
53
  export type DownloadedMySQLVersion = {
64
54
  version: string;
65
55
  path: string;
@@ -76,3 +66,17 @@ export type OptionTypeChecks = {
76
66
  definedType: "string" | "boolean" | "number";
77
67
  };
78
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.8.2",
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 --testPathIgnorePatterns=/stress-tests/ --verbose --colors",
20
- "test:ci": "jest --testPathIgnorePatterns=/stress-tests/ --setupFilesAfterEnv ./ciSetup.js --verbose --colors",
21
- "stress": "jest --runTestsByPath stress-tests/stress.test.ts --setupFilesAfterEnv ./ciSetup.js --verbose --colors"
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",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "adm-zip": "^0.5.16",
43
- "semver": "^7.6.3",
43
+ "semver": "^7.7.1",
44
44
  "signal-exit": "^4.1.0"
45
45
  },
46
46
  "repository": {