mysqlsyncr 1.0.32 → 1.0.36

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.
@@ -25,136 +25,137 @@ describe('Create Table Parser', () => {
25
25
  CONSTRAINT \`CaseDX\` FOREIGN KEY (\`idCaseDX\`) REFERENCES \`Case_DX\` (\`idCaseDX\`) ON DELETE NO ACTION ON UPDATE NO ACTION
26
26
  ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Keeps track of all active/inactive alarm rules used for the assets.\n';`;
27
27
 
28
- const expectedOutput = {
28
+ const expectedOutput = {
29
29
  "name": "AlarmRuleHistory",
30
30
  "columns": [
31
- {
32
- "Field": "idAlarmHistory",
33
- "Type": "int(11)",
34
- "NotNull": true,
35
- "AutoIncrement": true
36
- },
37
- {
38
- "Field": "idAsset",
39
- "Type": "int(11)",
40
- "NotNull": true
41
- },
42
- {
43
- "Field": "idCaseDX",
44
- "Type": "int(11)",
45
- "Default": "NULL"
46
- },
47
- {
48
- "Field": "idAlertRule",
49
- "Type": "int(11)",
50
- "Default": "NULL"
51
- },
52
- {
53
- "Field": "activeAlarmRule",
54
- "Type": "smallint(1)",
55
- "NotNull": true,
56
- "Default": "'0'"
57
- },
58
- {
59
- "Field": "alarmRule_StartDate",
60
- "Type": "timestamp",
61
- "Default": "NULL"
62
- },
63
- {
64
- "Field": "alarmRule_EndDate",
65
- "Type": "timestamp",
66
- "Default": "NULL"
67
- },
68
- {
69
- "Field": "dateCreated",
70
- "Type": "timestamp",
71
- "NotNull": true,
72
- "Default": "CURRENT_TIMESTAMP"
73
- },
74
- {
75
- "Field": "dateUpdated",
76
- "Type": "timestamp",
77
- "NotNull": true,
78
- "Default": "CURRENT_TIMESTAMP"
79
- },
80
- {
81
- "Field": "lastModifiedBy",
82
- "Type": "varchar(32)",
83
- "NotNull": true,
84
- "Default": "'System"
85
- }
31
+ {
32
+ "Field": "idAlarmHistory",
33
+ "Type": "int(11)",
34
+ "NotNull": true,
35
+ "AutoIncrement": true
36
+ },
37
+ {
38
+ "Field": "idAsset",
39
+ "Type": "int(11)",
40
+ "NotNull": true
41
+ },
42
+ {
43
+ "Field": "idCaseDX",
44
+ "Type": "int(11)",
45
+ "Default": "NULL"
46
+ },
47
+ {
48
+ "Field": "idAlertRule",
49
+ "Type": "int(11)",
50
+ "Default": "NULL"
51
+ },
52
+ {
53
+ "Field": "activeAlarmRule",
54
+ "Type": "smallint(1)",
55
+ "NotNull": true,
56
+ "Default": "0"
57
+ },
58
+ {
59
+ "Field": "alarmRule_StartDate",
60
+ "Type": "timestamp",
61
+ "Default": "NULL"
62
+ },
63
+ {
64
+ "Field": "alarmRule_EndDate",
65
+ "Type": "timestamp",
66
+ "Default": "NULL"
67
+ },
68
+ {
69
+ "Field": "dateCreated",
70
+ "Type": "timestamp",
71
+ "NotNull": true,
72
+ "Default": "CURRENT_TIMESTAMP"
73
+ },
74
+ {
75
+ "Field": "dateUpdated",
76
+ "Type": "timestamp",
77
+ "NotNull": true,
78
+ "Default": "CURRENT_TIMESTAMP"
79
+ },
80
+ {
81
+ "Field": "lastModifiedBy",
82
+ "Type": "varchar(32)",
83
+ "NotNull": true,
84
+ "Default": "System"
85
+ }
86
86
  ],
87
87
  "indexes": [
88
- {
89
- "ColumnName": [
90
- "`idAlarmHistory`"
91
- ],
92
- "Primary": true
93
- },
94
- {
95
- "ColumnName": [
96
- "`idAlertRule`"
97
- ],
98
- "Name": "AlertRule_idx"
99
- },
100
- {
101
- "ColumnName": [
102
- "`idCaseDX`"
103
- ],
104
- "Name": "CaseDX_idx"
105
- },
106
- {
107
- "ColumnName": [
108
- "`idAsset`"
109
- ],
110
- "Name": "Asset_idx"
111
- }
88
+ {
89
+ "ColumnName": [
90
+ "idAlarmHistory"
91
+ ],
92
+ "Primary": true,
93
+ "Name": "PRIMARY"
94
+ },
95
+ {
96
+ "ColumnName": [
97
+ "idAlertRule"
98
+ ],
99
+ "Name": "AlertRule_idx"
100
+ },
101
+ {
102
+ "ColumnName": [
103
+ "idCaseDX"
104
+ ],
105
+ "Name": "CaseDX_idx"
106
+ },
107
+ {
108
+ "ColumnName": [
109
+ "idAsset"
110
+ ],
111
+ "Name": "Asset_idx"
112
+ }
112
113
  ],
113
114
  "constraints": [
114
- {
115
- "Name": "AlertRule",
116
- "Type": "FOREIGN KEY",
117
- "ColumnName": [
118
- "`idAlertRule`"
119
- ],
120
- "References": {
121
- "Table": "`AlertRules`",
122
- "Column": "`idAlertRule`"
123
- },
124
- "OnDelete": "NO ACTION",
125
- "OnUpdate": "NO ACTION"
115
+ {
116
+ "Name": "AlertRule",
117
+ "Type": "FOREIGN KEY",
118
+ "ColumnName": [
119
+ "idAlertRule"
120
+ ],
121
+ "References": {
122
+ "Table": "AlertRules",
123
+ "Column": "idAlertRule"
124
+ },
125
+ "OnDelete": "NO ACTION",
126
+ "OnUpdate": "NO ACTION"
127
+ },
128
+ {
129
+ "Name": "AlertRule",
130
+ "Type": "FOREIGN KEY",
131
+ "ColumnName": [
132
+ "idAsset"
133
+ ],
134
+ "References": {
135
+ "Table": "Assets",
136
+ "Column": "idAsset"
126
137
  },
127
- {
128
- "Name": "AlertRule",
129
- "Type": "FOREIGN KEY",
130
- "ColumnName": [
131
- "`idAsset`"
132
- ],
133
- "References": {
134
- "Table": "`Assets`",
135
- "Column": "`idAsset`"
136
- },
137
- "OnDelete": "NO ACTION",
138
- "OnUpdate": "NO ACTION"
138
+ "OnDelete": "NO ACTION",
139
+ "OnUpdate": "NO ACTION"
140
+ },
141
+ {
142
+ "Name": "AlertRule",
143
+ "Type": "FOREIGN KEY",
144
+ "ColumnName": [
145
+ "idCaseDX"
146
+ ],
147
+ "References": {
148
+ "Table": "Case_DX",
149
+ "Column": "idCaseDX"
139
150
  },
140
- {
141
- "Name": "AlertRule",
142
- "Type": "FOREIGN KEY",
143
- "ColumnName": [
144
- "`idCaseDX`"
145
- ],
146
- "References": {
147
- "Table": "`Case_DX`",
148
- "Column": "`idCaseDX`"
149
- },
150
- "OnDelete": "NO ACTION",
151
- "OnUpdate": "NO ACTION"
152
- }
151
+ "OnDelete": "NO ACTION",
152
+ "OnUpdate": "NO ACTION"
153
+ }
153
154
  ],
154
155
  "engine": "InnoDB",
155
156
  "charset": "latin1",
156
157
  "collate": null
157
- }
158
+ }
158
159
 
159
160
 
160
161
 
@@ -0,0 +1,153 @@
1
+ import { parseCreateTableSQL, parseDatabaseStructure } from '../src/parsers';
2
+
3
+
4
+
5
+ describe('Create Table Parser', () => {
6
+
7
+ test('should correctly parse a valid create table statement. Check #1', () => {
8
+ const input = `CREATE TABLE \`Alarms\` (
9
+ \`alarmId\` int NOT NULL AUTO_INCREMENT,
10
+ \`alarmRuleId\` int NOT NULL,
11
+ \`alarmStatus\` int NOT NULL,
12
+ \`alarmStatus_Text\` varchar(50) NOT NULL,
13
+ \`alarmCreated_Date\` timestamp NULL DEFAULT NULL,
14
+ \`alarmConfirmed_Date\` timestamp NULL DEFAULT NULL,
15
+ \`alarmRecall_Date\` timestamp NULL DEFAULT NULL,
16
+ \`activeAlarm\` tinyint(1) NOT NULL DEFAULT '0',
17
+ \`emailSent\` tinyint(1) DEFAULT '0',
18
+ \`emailSent_AnswerPlus\` tinyint(1) DEFAULT '0',
19
+ \`ticketEmailSent\` tinyint(1) DEFAULT '0',
20
+ \`type\` varchar(45) DEFAULT NULL,
21
+ \`oorValue\` float(10,2) DEFAULT NULL,
22
+ \`thresholdValue\` float(10,2) DEFAULT NULL,
23
+ \`dateCreated\` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
24
+ \`dateUpdated\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
25
+ PRIMARY KEY (\`alarmId\`),
26
+ UNIQUE KEY \`UniqueAlarmRules\` (\`alarmRuleId\`),
27
+ KEY \`idx_alarms_alarmruleid_alarmstatus\` (\`alarmRuleId\`, \`alarmStatus\`)
28
+ ) ENGINE=InnoDB AUTO_INCREMENT=117 DEFAULT CHARSET=latin1 COMMENT='Alarm table for alarms '`;
29
+
30
+
31
+ const expectedOutput = {
32
+ "name": "Alarms",
33
+ "columns": [
34
+ {
35
+ "Field": "alarmId",
36
+ "Type": "int",
37
+ "NotNull": true,
38
+ "AutoIncrement": true
39
+ },
40
+ {
41
+ "Field": "alarmRuleId",
42
+ "Type": "int",
43
+ "NotNull": true
44
+ },
45
+ {
46
+ "Field": "alarmStatus",
47
+ "Type": "int",
48
+ "NotNull": true
49
+ },
50
+ {
51
+ "Field": "alarmStatus_Text",
52
+ "Type": "varchar(50)",
53
+ "NotNull": true
54
+ },
55
+ {
56
+ "Field": "alarmCreated_Date",
57
+ "Type": "timestamp",
58
+ "Default": "NULL"
59
+ },
60
+ {
61
+ "Field": "alarmConfirmed_Date",
62
+ "Type": "timestamp",
63
+ "Default": "NULL"
64
+ },
65
+ {
66
+ "Field": "alarmRecall_Date",
67
+ "Type": "timestamp",
68
+ "Default": "NULL"
69
+ },
70
+ {
71
+ "Field": "activeAlarm",
72
+ "Type": "tinyint(1)",
73
+ "NotNull": true,
74
+ "Default": "0"
75
+ },
76
+ {
77
+ "Field": "emailSent",
78
+ "Type": "tinyint(1)",
79
+ "Default": "0"
80
+ },
81
+ {
82
+ "Field": "emailSent_AnswerPlus",
83
+ "Type": "tinyint(1)",
84
+ "Default": "0"
85
+ },
86
+ {
87
+ "Field": "ticketEmailSent",
88
+ "Type": "tinyint(1)",
89
+ "Default": "0"
90
+ },
91
+ {
92
+ "Field": "type",
93
+ "Type": "varchar(45)",
94
+ "Default": "NULL"
95
+ },
96
+ {
97
+ "Field": "oorValue",
98
+ "Type": "float(10,2)"
99
+ },
100
+ {
101
+ "Field": "thresholdValue",
102
+ "Type": "float(10,2)"
103
+ },
104
+ {
105
+ "Field": "dateCreated",
106
+ "Type": "timestamp",
107
+ "Default": "CURRENT_TIMESTAMP"
108
+ },
109
+ {
110
+ "Field": "dateUpdated",
111
+ "Type": "timestamp",
112
+ "NotNull": true,
113
+ "Default": "CURRENT_TIMESTAMP"
114
+ }
115
+ ],
116
+ "indexes": [
117
+ {
118
+ "ColumnName": [
119
+ "alarmId"
120
+ ],
121
+ "Primary": true,
122
+ "Name": "PRIMARY"
123
+ },
124
+ {
125
+ "ColumnName": [
126
+ "alarmRuleId"
127
+ ],
128
+ "Unique": true,
129
+ "Name": "UniqueAlarmRules"
130
+ },
131
+ {
132
+ "ColumnName": [
133
+ "alarmRuleId",
134
+ "alarmStatus"
135
+ ],
136
+ "Name": "idx_alarms_alarmruleid_alarmstatus"
137
+ }
138
+ ],
139
+ "constraints": [],
140
+ "engine": null,
141
+ "charset": null,
142
+ "collate": null
143
+ }
144
+
145
+
146
+
147
+ const result = parseCreateTableSQL(input);
148
+ console.log('Result:', JSON.stringify(result, null, 2))
149
+ expect(result).toEqual(expectedOutput);
150
+ });
151
+
152
+
153
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mysqlsyncr",
3
- "version": "1.0.32",
3
+ "version": "1.0.36",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/apply.js CHANGED
@@ -65,6 +65,16 @@ export const applyDifferences = async (connection, database, differences) => {
65
65
  console.log(`Executing: ${modifyFieldSQL}`);
66
66
  await connection.query(modifyFieldSQL);
67
67
  }
68
+ if(diff.field.Default){
69
+ const defaultClause = diff.field.Default === 'CURRENT_TIMESTAMP'
70
+ ? 'DEFAULT CURRENT_TIMESTAMP'
71
+ : diff.field.Default !== undefined && diff.field.Default !== null
72
+ ? `DEFAULT '${diff.field.Default}'`
73
+ : '';
74
+ const modifyFieldSQL = `ALTER TABLE \`${diff.tableName}\` MODIFY \`${diff.field.Field}\` ${diff.field.Type} ${defaultClause};`;
75
+ console.log(`Executing: ${modifyFieldSQL}`);
76
+ await connection.query(modifyFieldSQL);
77
+ }
68
78
 
69
79
  } else if (diff.type === 'extra_field') {
70
80
  const dropFieldSQL = `ALTER TABLE \`${diff.tableName}\` DROP COLUMN \`${diff.field.Field}\`;`;
@@ -38,12 +38,12 @@ export const findDifferences = (expected, current) => {
38
38
  const expectedFieldNames = columns.map(f => f.Field);
39
39
 
40
40
  logger('Detecting missing fields for table', tableName);
41
- // Detect missing fields
42
- for (const field of columns) {
43
- if (!currentFieldNames.includes(field.Field)) {
44
- differences.push({ type: 'missing_field', tableName, field });
45
- }
46
- }
41
+ // // Detect missing fields
42
+ // for (const field of columns) {
43
+ // if (!currentFieldNames.includes(field.Field)) {
44
+ // differences.push({ type: 'missing_field', tableName, field });
45
+ // }
46
+ // }
47
47
 
48
48
  logger('Detecting extra fields for table', tableName);
49
49
  // Detect extra fields
package/src/dump.js CHANGED
@@ -1,7 +1,11 @@
1
+ import { createRequire } from 'module';
1
2
  import fs from 'fs/promises';
2
3
  import path from 'path';
3
4
  import { getDatabases, getDatabaseStructure } from './getters.js';
4
5
 
6
+ const require = createRequire(import.meta.url);
7
+ const { version } = require('../package.json');
8
+
5
9
  export const dumpAllDatabases = async (connection, command, options) => {
6
10
  let response = {}
7
11
  const databases = await getDatabases(connection);
@@ -16,6 +20,8 @@ export const dumpAllDatabases = async (connection, command, options) => {
16
20
  await fs.writeFile(path.join(options.output , database, `${database}.json`), JSON.stringify(dbStructure, null, 4), 'utf-8');
17
21
  response[database] = dbStructure;
18
22
  }
23
+
24
+ await fs.writeFile(path.join(options.output, 'dumpInfo.json'), JSON.stringify({timestamp: new Date().toISOString(), version: version}, null, 4), 'utf-8');
19
25
  return response;
20
26
  }
21
27
 
@@ -33,7 +39,7 @@ export const getDatabasesFromExistingDumps = async (options) => {
33
39
  let response = {}
34
40
  const databases = await fs.readdir(path.join(options.output));
35
41
  for (const database of databases) {
36
- if(database.includes('diffs')){
42
+ if(database.includes('diffs') || database.includes('dumpInfo')){
37
43
  console.log('Skipping diffs folder')
38
44
  continue
39
45
  }
package/src/index.js CHANGED
@@ -14,17 +14,19 @@ class DBStructureChecker {
14
14
  constructor(config, targetDatabase = null) {
15
15
  this.config = config;
16
16
  this.targetDatabase = targetDatabase;
17
- this.connection = mysql.createConnection(this.config);
17
+ this.connection = mysql.createConnection(this.config)
18
18
  this.dumpAllDatabases = dumpAllDatabases;
19
19
  this.compareAllDatabases = compareAllDatabases;
20
20
  this.getDatabases = () => {
21
21
 
22
22
  fs.readFileSync(path.join(__dirname, 'databases.json'), 'utf8');
23
23
  }
24
- this.connection.on('error', (err) => {
25
- console.error('Database connection error:', err);
26
- });
24
+ // this.connection.on('error', (err) => {
25
+ // this.emit('error', err);
26
+ // console.error('Database connection error:', err);
27
+ // });
27
28
  this.connection.query = util.promisify(this.connection.query); // Promisify for async/await
29
+
28
30
  }
29
31
 
30
32
  async reconnect() {
package/src/parsers.js CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  export const parseCreateTableSQL = (createTableSQL) => {
3
2
  if(!createTableSQL) return null;
4
3
  const name = createTableSQL.match(/CREATE TABLE\s+`([^`]+)`/)[1];
@@ -32,9 +31,10 @@ export const parseCreateTableSQL = (createTableSQL) => {
32
31
  const indexMatch = indexRegex.exec(line);
33
32
  const constraintMatch = parseConstraint(line);
34
33
  if (columnMatch && !indexMatch && !constraintMatch) {
34
+ const columnType = parseColumnType(line);
35
35
  const column = {
36
36
  Field: columnMatch[1],
37
- Type: columnMatch[2]
37
+ Type: columnType
38
38
  };
39
39
 
40
40
  // Set Null property based on NOT NULL presence
@@ -116,15 +116,30 @@ export const parseConstraint = (sql) => {
116
116
  }else{
117
117
  onUpdate = 'NO ACTION';
118
118
  }
119
+ let columnName = match[1] || ''
120
+ columnName = columnName.replace(/`/g, '')
121
+ let table = match[2] || ''
122
+ table = table.replace(/`/g, '')
123
+ let column = match[3] || ''
124
+ column = column.replace(/`/g, '')
119
125
  return {
120
126
  Name: "AlertRule", // Replace with actual constraint name if needed
121
127
  Type: "FOREIGN KEY",
122
- ColumnName: [match[1]], // Column in the current table
128
+ ColumnName: [columnName], // Column in the current table
123
129
  References: {
124
- Table: match[2], // Referenced table
125
- Column: match[3], // Referenced column
130
+ Table: table, // Referenced table
131
+ Column: column, // Referenced column
126
132
  },
127
133
  OnDelete: onDelete,
128
134
  OnUpdate: onUpdate,
129
135
  };
130
- };
136
+ };
137
+
138
+ const parseColumnType = (line) => {
139
+ // Match type pattern including parameters, handling both single and double parameter types
140
+ const typeRegex = /`[^`]+`\s+([a-zA-Z]+(?:\([0-9]+(?:,[0-9]+)?\))?)/i;
141
+ const match = line.match(typeRegex);
142
+
143
+ if (!match) return null;
144
+ return match[1]; // Returns the full type including parameters
145
+ };