mysqlsyncr 1.1.12 → 1.2.1

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.
Files changed (62) hide show
  1. package/README.md +59 -0
  2. package/__tests__/acceptCurrent.test.js +117 -0
  3. package/__tests__/columnAttributes.test.js +101 -0
  4. package/__tests__/options.test.js +259 -0
  5. package/__tests__/parser.test.js +167 -166
  6. package/__tests__/parser2.test.js +155 -152
  7. package/__tests__/paths.test.js +21 -0
  8. package/__tests__/prompt.test.js +106 -0
  9. package/__tests__/sync.test.js +348 -0
  10. package/__tests__/templateDrift.test.js +72 -0
  11. package/__tests__/versions.test.js +138 -0
  12. package/dist/index.cjs +4059 -1733
  13. package/dist/index.js +4101 -1777
  14. package/package.json +66 -59
  15. package/rollup.config.js +2 -1
  16. package/src/DBStructureChecker.js +47 -0
  17. package/src/acceptCurrent.js +225 -0
  18. package/src/apply.js +254 -136
  19. package/src/cli.js +61 -34
  20. package/src/compare.js +106 -92
  21. package/src/differences.js +258 -212
  22. package/src/dump.js +63 -54
  23. package/src/getters.js +151 -139
  24. package/src/index.js +223 -169
  25. package/src/loggers.js +156 -139
  26. package/src/normalizers.js +28 -6
  27. package/src/options.js +133 -0
  28. package/src/parsers.js +82 -36
  29. package/src/paths.js +41 -0
  30. package/src/prompt.js +109 -0
  31. package/src/rebuild.js +106 -0
  32. package/src/rows.js +46 -0
  33. package/src/socketServer.js +106 -24
  34. package/src/templateDrift.js +93 -0
  35. package/src/versions.js +112 -0
  36. package/ui/README.md +55 -40
  37. package/ui/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjalmUiAw-k1h7X_-h.woff +0 -0
  38. package/ui/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtalmUiAw-B7du-70m.woff +0 -0
  39. package/ui/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabVmUiAw-CoAZ_DKt.woff +0 -0
  40. package/ui/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bVmUiAw-D0406B4n.woff +0 -0
  41. package/ui/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbFmUiAw-CnAg2DeQ.woff +0 -0
  42. package/ui/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAw-BG9VWE5v.woff +0 -0
  43. package/ui/dist/spa/assets/MainLayout-DLH1Kfg-.js +1 -0
  44. package/ui/dist/spa/assets/axios-2hQ_j_Tz.js +1 -0
  45. package/ui/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-OUIwM9U8.woff +0 -0
  46. package/ui/dist/spa/assets/index-Cqrg985P.js +23 -0
  47. package/ui/dist/spa/assets/index-pOWtXlXQ.css +1 -0
  48. package/ui/dist/spa/index.html +2 -2
  49. package/ui/package.json +43 -42
  50. package/ui/src/components/DefinitionDiff.vue +169 -0
  51. package/ui/src/pages/MainComponent.vue +704 -78
  52. package/ui/dist/spa/assets/KFOkCnqEu92Fr1MmgVxIIzQ-Y2mpUUkw.woff +0 -0
  53. package/ui/dist/spa/assets/KFOlCnqEu92Fr1MmEU9fBBc--ViM7Ag4Q.woff +0 -0
  54. package/ui/dist/spa/assets/KFOlCnqEu92Fr1MmSU5fBBc--CrFzpgl6.woff +0 -0
  55. package/ui/dist/spa/assets/KFOlCnqEu92Fr1MmWUlfBBc--CW3_XjG_.woff +0 -0
  56. package/ui/dist/spa/assets/KFOlCnqEu92Fr1MmYUtfBBc--Dr8fnyGc.woff +0 -0
  57. package/ui/dist/spa/assets/KFOmCnqEu92Fr1Mu4mxM-DAYh6T4l.woff +0 -0
  58. package/ui/dist/spa/assets/MainLayout-CvQD3xKr.js +0 -1
  59. package/ui/dist/spa/assets/axios-C0XcclIb.js +0 -1
  60. package/ui/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
  61. package/ui/dist/spa/assets/index-CwWo8xmP.js +0 -40
  62. package/ui/dist/spa/assets/index-kXpA8n8r.css +0 -5
@@ -0,0 +1,138 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import { normalizeVersionFormatting } from '../src/normalizers';
5
+ import { getTemplateVersion, versionNotes, adaptSQLForServer, majorMinor } from '../src/versions';
6
+ import { findDifferences } from '../src/differences';
7
+ import { applyDifferences } from '../src/apply';
8
+ import { parseCreateTableSQL } from '../src/parsers';
9
+
10
+ const structure = (createSQL, procedures = []) => {
11
+ const parsed = parseCreateTableSQL(createSQL);
12
+ return { tables: { [parsed.name]: { ...parsed, triggers: [], createSQL } }, procedures };
13
+ };
14
+
15
+ // The same table as SHOW CREATE TABLE prints it on each version
16
+ const mysql57 = `CREATE TABLE \`Readings\` (
17
+ \`id\` int(11) NOT NULL AUTO_INCREMENT,
18
+ \`count\` bigint(20) unsigned NOT NULL DEFAULT '0',
19
+ \`active\` tinyint(1) NOT NULL DEFAULT '1',
20
+ \`level\` tinyint(4) DEFAULT NULL,
21
+ \`made\` year(4) DEFAULT NULL,
22
+ \`code\` int(5) unsigned zerofill DEFAULT NULL,
23
+ \`name\` varchar(45) CHARACTER SET utf8 DEFAULT NULL,
24
+ PRIMARY KEY (\`id\`)
25
+ ) ENGINE=InnoDB DEFAULT CHARSET=latin1`;
26
+ const mysql80 = `CREATE TABLE \`Readings\` (
27
+ \`id\` int NOT NULL AUTO_INCREMENT,
28
+ \`count\` bigint unsigned NOT NULL DEFAULT '0',
29
+ \`active\` tinyint(1) NOT NULL DEFAULT '1',
30
+ \`level\` tinyint DEFAULT NULL,
31
+ \`made\` year DEFAULT NULL,
32
+ \`code\` int(5) unsigned zerofill DEFAULT NULL,
33
+ \`name\` varchar(45) CHARACTER SET utf8mb3 DEFAULT NULL,
34
+ PRIMARY KEY (\`id\`)
35
+ ) ENGINE=InnoDB DEFAULT CHARSET=latin1`;
36
+
37
+ describe('formatting that differs between MySQL 5.7 and 8.0', () => {
38
+ test('integer widths and year(4) are dropped, except tinyint(1) and zerofill', () => {
39
+ expect(normalizeVersionFormatting('int(11)')).toBe('int');
40
+ expect(normalizeVersionFormatting('bigint(20) unsigned')).toBe('bigint unsigned');
41
+ expect(normalizeVersionFormatting('tinyint(1)')).toBe('tinyint(1)');
42
+ expect(normalizeVersionFormatting('int(5) unsigned zerofill')).toBe('int(5) unsigned zerofill');
43
+ expect(normalizeVersionFormatting('year(4)')).toBe('year');
44
+ expect(normalizeVersionFormatting('varchar(10) CHARSET utf8mb3 COLLATE utf8mb3_bin')).toBe('varchar(10) CHARSET utf8 COLLATE utf8_bin');
45
+ });
46
+
47
+ test('a 5.7 table matches the same table on 8.0, both ways', () => {
48
+ expect(findDifferences(structure(mysql80 + ';'), structure(mysql57))).toEqual([]);
49
+ expect(findDifferences(structure(mysql57 + ';'), structure(mysql80))).toEqual([]);
50
+ });
51
+
52
+ test('real type changes are still reported', () => {
53
+ const changed = mysql57.replace('bigint(20) unsigned', 'bigint(20)').replace('tinyint(1) NOT NULL', 'tinyint(4) NOT NULL');
54
+ const diffs = findDifferences(structure(mysql80 + ';'), structure(changed));
55
+ expect(diffs.map(d => `${d.field.Field} ${d.info}`).sort()).toEqual(['active Type', 'count Type']);
56
+ });
57
+
58
+ test('procedures printed by each version match', () => {
59
+ const proc57 = 'CREATE DEFINER=`root`@`%` PROCEDURE `P`(IN a int(11), IN b varchar(10) CHARSET utf8)\nBEGIN\n SELECT a;\nEND';
60
+ const proc80 = 'CREATE DEFINER=`root`@`%` PROCEDURE `P`(IN a int, IN b varchar(10) CHARSET utf8mb3)\nBEGIN\n SELECT a;\nEND';
61
+ expect(findDifferences({ tables: {}, procedures: [{ Name: 'P', Definition: proc80 }] }, { tables: {}, procedures: [{ Name: 'P', Definition: proc57 }] })).toEqual([]);
62
+ });
63
+ });
64
+
65
+ describe('template and server versions', () => {
66
+ const tempDir = () => fs.mkdtempSync(path.join(os.tmpdir(), 'mysqlsyncr-version-'));
67
+ const writeTemplate = (dir, createSQL) => {
68
+ fs.mkdirSync(path.join(dir, 'Alerts'));
69
+ fs.writeFileSync(path.join(dir, 'Alerts', 'Alerts.json'), JSON.stringify(structure(createSQL)));
70
+ };
71
+
72
+ test('uses the version dump recorded', async () => {
73
+ const dir = tempDir();
74
+ fs.writeFileSync(path.join(dir, 'dumpInfo.json'), JSON.stringify({ timestamp: 'x', mysqlVersion: '8.0.36' }));
75
+ expect(await getTemplateVersion(dir)).toEqual({ version: '8.0.36', inferred: false });
76
+ });
77
+
78
+ test('infers it from older templates', async () => {
79
+ const dir57 = tempDir();
80
+ writeTemplate(dir57, mysql57.replace(/ CHARACTER SET utf8/, ''));
81
+ expect(await getTemplateVersion(dir57)).toMatchObject({ version: '5.7', inferred: true });
82
+ const dir80 = tempDir();
83
+ writeTemplate(dir80, mysql80);
84
+ expect(await getTemplateVersion(dir80)).toMatchObject({ version: '8.0', inferred: true });
85
+ });
86
+
87
+ test('explains what differs between versions', () => {
88
+ expect(majorMinor('5.7.44-log')).toBe('5.7');
89
+ expect(versionNotes('8.0.36', '8.0.19')).toEqual([]);
90
+ expect(versionNotes('8.0', '5.7.44')).toHaveLength(3);
91
+ expect(versionNotes('5.7.44', '8.0.36')).toHaveLength(1);
92
+ expect(versionNotes(undefined, '8.0.36')).toEqual([]);
93
+ });
94
+ });
95
+
96
+ describe('applying an 8.0 template to a 5.7 server', () => {
97
+ test('8.0 collations are swapped for ones 5.7 has', () => {
98
+ expect(adaptSQLForServer('DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci', '5.7.44')).toBe('DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci');
99
+ expect(adaptSQLForServer('COLLATE utf8mb4_0900_bin', '5.7.44')).toBe('COLLATE utf8mb4_bin');
100
+ expect(adaptSQLForServer('COLLATE utf8mb4_0900_ai_ci', '8.0.36')).toBe('COLLATE utf8mb4_0900_ai_ci');
101
+ });
102
+
103
+ test('statements are adapted for the server they run on', async () => {
104
+ const statements = [];
105
+ const connection = {
106
+ query: async (sql) => {
107
+ if (sql.startsWith('SELECT VERSION')) return [{ version: '5.7.44-log' }];
108
+ if (!sql.startsWith('USE') && !sql.startsWith('SET SESSION')) statements.push(sql);
109
+ return [];
110
+ }
111
+ };
112
+ await applyDifferences(connection, 'Alerts', [
113
+ { type: 'missing_table', tableName: 'T', createSQL: 'CREATE TABLE `T` (\n `a` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci' }
114
+ ]);
115
+ expect(statements).toEqual(['CREATE TABLE `T` (\n `a` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci']);
116
+ });
117
+ });
118
+
119
+ describe('when the server version is unknown', () => {
120
+ test('an unknown collation error is retried with the closest collation and reported as a warning', async () => {
121
+ const statements = [];
122
+ const connection = {
123
+ query: async (sql) => {
124
+ if (sql.startsWith('SELECT VERSION')) throw new Error('not allowed');
125
+ if (sql.startsWith('USE') || sql.startsWith('SET SESSION')) return [];
126
+ statements.push(sql);
127
+ if (sql.includes('utf8mb4_0900_ai_ci')) throw new Error("Unknown collation: 'utf8mb4_0900_ai_ci'");
128
+ return [];
129
+ }
130
+ };
131
+ const result = await applyDifferences(connection, 'Alerts', [
132
+ { type: 'missing_table', tableName: 'T', createSQL: 'CREATE TABLE `T` (\n `a` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci' }
133
+ ]);
134
+ expect(statements[1]).toContain('COLLATE=utf8mb4_unicode_ci');
135
+ expect(result.failures).toEqual([]);
136
+ expect(result.warnings).toEqual([expect.objectContaining({ type: 'missing_table', tableName: 'T', message: expect.stringContaining('utf8mb4_unicode_ci') })]);
137
+ });
138
+ });