mysqlsyncr 1.1.4 → 1.1.6

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/dist/index.cjs CHANGED
@@ -137,7 +137,10 @@ function _iterableToArrayLimit(r, l) {
137
137
  f = true,
138
138
  o = false;
139
139
  try {
140
- if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
140
+ if (i = (t = t.call(r)).next, 0 === l) {
141
+ if (Object(t) !== t) return;
142
+ f = !1;
143
+ } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
141
144
  } catch (r) {
142
145
  o = true, n = r;
143
146
  } finally {
@@ -3599,7 +3602,7 @@ var applyDifferences = /*#__PURE__*/function () {
3599
3602
  _iterator = _createForOfIteratorHelper(differences);
3600
3603
  _context2.prev = 2;
3601
3604
  _loop = /*#__PURE__*/_regeneratorRuntime().mark(function _loop() {
3602
- var diff, createDatabaseSQL, tableName, createSQL, _tableName, dropTableSQL, addFieldSQL, modifyFieldSQL, defaultClause, _modifyFieldSQL, dropFieldSQL, _tableName2, field, isTimestamp, _defaultClause, currentField, hasOnUpdate, _dropFieldSQL, existingColumns, columnNames, missingColumns, indexColumns, primaryKeySQL, uniqueSQL, addIndexSQL, dropPrimaryKeySQL, dropIndexSQL, _tableName3, index, _dropPrimaryKeySQL, _dropIndexSQL, _uniqueSQL, addPrimaryKeySQL, _addIndexSQL, dropCurrentProcSQL, createProcSQL, dropViewSQL, createViewSQL, _dropViewSQL, _createViewSQL, _dropViewSQL2, createTriggerSQL, dropTriggerSQL, _createTriggerSQL, _dropTriggerSQL, _createDatabaseSQL;
3605
+ var diff, createDatabaseSQL, tableName, createSQL, _tableName, dropTableSQL, addFieldSQL, modifyFieldSQL, defaultClause, _modifyFieldSQL, dropFieldSQL, _tableName2, field, currentField, needsBatchProcessing, addColumnSQL, totalUpdated, batchSize, batchCount, updateSQL, result, rowsAffected, verifySQL, _yield$connection$que, _yield$connection$que2, verifyResult, swapColumnsSQL, isTimestamp, _defaultClause, _currentField, hasOnUpdate, _modifyFieldSQL2, existingColumns, columnNames, missingColumns, indexColumns, primaryKeySQL, uniqueSQL, addIndexSQL, dropPrimaryKeySQL, dropIndexSQL, _tableName3, index, _dropPrimaryKeySQL, _dropIndexSQL, _uniqueSQL, addPrimaryKeySQL, _addIndexSQL, dropCurrentProcSQL, createProcSQL, dropViewSQL, createViewSQL, _dropViewSQL, _createViewSQL, _dropViewSQL2, createTriggerSQL, dropTriggerSQL, _createTriggerSQL, _dropTriggerSQL, _createDatabaseSQL;
3603
3606
  return _regeneratorRuntime().wrap(function _loop$(_context) {
3604
3607
  while (1) switch (_context.prev = _context.next) {
3605
3608
  case 0:
@@ -3629,7 +3632,7 @@ var applyDifferences = /*#__PURE__*/function () {
3629
3632
  _context.next = 16;
3630
3633
  return connection.query(createSQL);
3631
3634
  case 16:
3632
- _context.next = 213;
3635
+ _context.next = 249;
3633
3636
  break;
3634
3637
  case 18:
3635
3638
  if (!(diff.type === 'extra_table')) {
@@ -3643,7 +3646,7 @@ var applyDifferences = /*#__PURE__*/function () {
3643
3646
  _context.next = 25;
3644
3647
  return connection.query(dropTableSQL);
3645
3648
  case 25:
3646
- _context.next = 213;
3649
+ _context.next = 249;
3647
3650
  break;
3648
3651
  case 27:
3649
3652
  if (!(diff.type === 'missing_field')) {
@@ -3675,7 +3678,7 @@ var applyDifferences = /*#__PURE__*/function () {
3675
3678
  _context.next = 43;
3676
3679
  return connection.query(_modifyFieldSQL);
3677
3680
  case 43:
3678
- _context.next = 213;
3681
+ _context.next = 249;
3679
3682
  break;
3680
3683
  case 45:
3681
3684
  if (!(diff.type === 'extra_field')) {
@@ -3687,33 +3690,132 @@ var applyDifferences = /*#__PURE__*/function () {
3687
3690
  _context.next = 50;
3688
3691
  return connection.query(dropFieldSQL);
3689
3692
  case 50:
3690
- _context.next = 213;
3693
+ _context.next = 249;
3691
3694
  break;
3692
3695
  case 52:
3693
3696
  if (!(diff.type === 'mismatched_field')) {
3694
- _context.next = 64;
3697
+ _context.next = 100;
3695
3698
  break;
3696
3699
  }
3697
- _tableName2 = diff.tableName, field = diff.field;
3700
+ _tableName2 = diff.tableName, field = diff.field, currentField = diff.currentField; // Helper function to determine if we need batch processing
3701
+ needsBatchProcessing = function needsBatchProcessing() {
3702
+ var currentType = currentField.Type.toLowerCase();
3703
+ var newType = field.Type.toLowerCase();
3704
+
3705
+ // Decimal changes
3706
+ if (currentType.includes('decimal') && newType.includes('decimal')) {
3707
+ return true;
3708
+ }
3709
+
3710
+ // VARCHAR/CHAR length reductions
3711
+ if ((currentType.includes('varchar') || currentType.includes('char')) && (newType.includes('varchar') || newType.includes('char'))) {
3712
+ var _currentType$match, _newType$match;
3713
+ var currentLength = parseInt(((_currentType$match = currentType.match(/\((\d+)\)/)) === null || _currentType$match === undefined ? undefined : _currentType$match[1]) || '0');
3714
+ var newLength = parseInt(((_newType$match = newType.match(/\((\d+)\)/)) === null || _newType$match === undefined ? undefined : _newType$match[1]) || '0');
3715
+ return newLength < currentLength;
3716
+ }
3717
+
3718
+ // TEXT to VARCHAR conversions
3719
+ if (currentType.includes('text') && newType.includes('varchar')) {
3720
+ return true;
3721
+ }
3722
+
3723
+ // Numeric type downgrades
3724
+ var numericDowngrades = {
3725
+ 'bigint': ['int', 'mediumint', 'smallint', 'tinyint'],
3726
+ 'int': ['mediumint', 'smallint', 'tinyint'],
3727
+ 'mediumint': ['smallint', 'tinyint'],
3728
+ 'smallint': ['tinyint'],
3729
+ 'double': ['float', 'decimal'],
3730
+ 'float': ['decimal']
3731
+ };
3732
+ for (var _i = 0, _Object$entries = Object.entries(numericDowngrades); _i < _Object$entries.length; _i++) {
3733
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
3734
+ larger = _Object$entries$_i[0],
3735
+ smaller = _Object$entries$_i[1];
3736
+ if (currentType.includes(larger) && smaller.some(function (t) {
3737
+ return newType.includes(t);
3738
+ })) {
3739
+ return true;
3740
+ }
3741
+ }
3742
+ return false;
3743
+ };
3744
+ if (!needsBatchProcessing()) {
3745
+ _context.next = 90;
3746
+ break;
3747
+ }
3748
+ // Step 1: Add new column
3749
+ addColumnSQL = "ALTER TABLE `".concat(_tableName2, "` ADD COLUMN `").concat(field.Field, "_new` ").concat(field.Type, " DEFAULT NULL;");
3750
+ logger("Adding new column for type conversion: ".concat(addColumnSQL));
3751
+ _context.next = 60;
3752
+ return connection.query(addColumnSQL);
3753
+ case 60:
3754
+ // Step 2: Copy data in batches
3755
+ totalUpdated = 0;
3756
+ batchSize = 10000;
3757
+ batchCount = 0;
3758
+ case 63:
3759
+ updateSQL = "\n UPDATE `".concat(_tableName2, "` \n SET `").concat(field.Field, "_new` = `").concat(field.Field, "`\n WHERE `").concat(field.Field, "_new` IS NULL \n AND `").concat(field.Field, "` IS NOT NULL\n LIMIT ").concat(batchSize, ";\n ");
3760
+ _context.next = 67;
3761
+ return connection.query(updateSQL);
3762
+ case 67:
3763
+ result = _context.sent;
3764
+ rowsAffected = result.affectedRows;
3765
+ if (!(rowsAffected === 0)) {
3766
+ _context.next = 71;
3767
+ break;
3768
+ }
3769
+ return _context.abrupt("break", 76);
3770
+ case 71:
3771
+ totalUpdated += rowsAffected;
3772
+ batchCount++;
3773
+ logger("Batch ".concat(batchCount, ": Converted ").concat(rowsAffected, " rows. Total converted: ").concat(totalUpdated));
3774
+ _context.next = 63;
3775
+ break;
3776
+ case 76:
3777
+ // Step 3: Verify data (optional)
3778
+ verifySQL = "\n SELECT COUNT(*) as mismatch FROM `".concat(_tableName2, "`\n WHERE `").concat(field.Field, "` IS NOT NULL \n AND `").concat(field.Field, "_new` IS NULL;\n ");
3779
+ _context.next = 79;
3780
+ return connection.query(verifySQL);
3781
+ case 79:
3782
+ _yield$connection$que = _context.sent;
3783
+ _yield$connection$que2 = _slicedToArray(_yield$connection$que, 1);
3784
+ verifyResult = _yield$connection$que2[0];
3785
+ if (verifyResult.mismatch > 0) {
3786
+ logger("Warning: ".concat(verifyResult.mismatch, " rows may have failed conversion"));
3787
+ }
3788
+
3789
+ // Step 4: Swap columns
3790
+ swapColumnsSQL = "\n ALTER TABLE `".concat(_tableName2, "`\n DROP COLUMN `").concat(field.Field, "`,\n RENAME COLUMN `").concat(field.Field, "_new` TO `").concat(field.Field, "`\n ");
3791
+ logger("Swapping columns: ".concat(swapColumnsSQL));
3792
+ _context.next = 87;
3793
+ return connection.query(swapColumnsSQL);
3794
+ case 87:
3795
+ logger("Completed type conversion for ".concat(field.Field, ". Total rows converted: ").concat(totalUpdated));
3796
+ _context.next = 98;
3797
+ break;
3798
+ case 90:
3799
+ // Original mismatched field handling for simple changes
3698
3800
  isTimestamp = field.Type.toLowerCase().includes('timestamp'); // Handle timestamp specific attributes
3699
3801
  _defaultClause = field.Default === 'CURRENT_TIMESTAMP' ? 'DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' : field.Default === 'NULL' ? isTimestamp ? 'NULL DEFAULT NULL' : 'DEFAULT NULL' : field.Default !== undefined && field.Default !== null ? "DEFAULT '".concat(field.Default, "'") : isTimestamp ? 'NULL DEFAULT NULL' : 'DEFAULT NULL'; // Remove ON UPDATE CURRENT_TIMESTAMP if we're changing away from CURRENT_TIMESTAMP
3700
- currentField = diff.currentField;
3701
- hasOnUpdate = (currentField === null || currentField === undefined ? undefined : currentField.Default) === 'CURRENT_TIMESTAMP' && field.Default !== 'CURRENT_TIMESTAMP';
3702
- _dropFieldSQL = "ALTER TABLE `".concat(_tableName2, "` CHANGE `").concat(field.Field, "` `").concat(field.Field, "` ").concat(field.Type, " ").concat(field.NotNull ? 'NOT NULL' : '', " ").concat(_defaultClause).concat(hasOnUpdate ? ' ON UPDATE CURRENT_TIMESTAMP' : '', ";");
3703
- logger("Executing: ".concat(_dropFieldSQL));
3704
- _context.next = 62;
3705
- return connection.query(_dropFieldSQL);
3706
- case 62:
3707
- _context.next = 213;
3802
+ _currentField = diff.currentField;
3803
+ hasOnUpdate = (_currentField === null || _currentField === undefined ? undefined : _currentField.Default) === 'CURRENT_TIMESTAMP' && field.Default !== 'CURRENT_TIMESTAMP';
3804
+ _modifyFieldSQL2 = "ALTER TABLE `".concat(_tableName2, "` CHANGE `").concat(field.Field, "` `").concat(field.Field, "` ").concat(field.Type, " ").concat(field.NotNull ? 'NOT NULL' : '', " ").concat(_defaultClause).concat(hasOnUpdate ? ' ON UPDATE CURRENT_TIMESTAMP' : '', ";");
3805
+ console.log("Executing: ".concat(_modifyFieldSQL2));
3806
+ _context.next = 98;
3807
+ return connection.query(_modifyFieldSQL2);
3808
+ case 98:
3809
+ _context.next = 249;
3708
3810
  break;
3709
- case 64:
3811
+ case 100:
3710
3812
  if (!(diff.type === 'missing_index')) {
3711
- _context.next = 96;
3813
+ _context.next = 132;
3712
3814
  break;
3713
3815
  }
3714
- _context.next = 67;
3816
+ _context.next = 103;
3715
3817
  return connection.query("SHOW COLUMNS FROM `".concat(diff.tableName, "`"));
3716
- case 67:
3818
+ case 103:
3717
3819
  existingColumns = _context.sent;
3718
3820
  columnNames = existingColumns.map(function (col) {
3719
3821
  return col.Field;
@@ -3722,260 +3824,260 @@ var applyDifferences = /*#__PURE__*/function () {
3722
3824
  return !columnNames.includes(col);
3723
3825
  }) : [];
3724
3826
  if (!(missingColumns.length > 0)) {
3725
- _context.next = 73;
3827
+ _context.next = 109;
3726
3828
  break;
3727
3829
  }
3728
3830
  logger("Cannot create index ".concat(diff.index.Name, " on ").concat(diff.tableName, ". Missing columns: ").concat(missingColumns.join(', ')));
3729
3831
  return _context.abrupt("return", 0);
3730
- case 73:
3832
+ case 109:
3731
3833
  // Prepare the index columns with proper formatting
3732
3834
  indexColumns = diff.index.ColumnName.map(function (col) {
3733
3835
  return "`".concat(col, "`");
3734
3836
  }); // Format column names
3735
3837
  // Handle PRIMARY index creation
3736
3838
  if (!(diff.index.Name === 'PRIMARY')) {
3737
- _context.next = 85;
3839
+ _context.next = 121;
3738
3840
  break;
3739
3841
  }
3740
3842
  if (!(indexColumns.length > 0)) {
3741
- _context.next = 82;
3843
+ _context.next = 118;
3742
3844
  break;
3743
3845
  }
3744
3846
  primaryKeySQL = "ALTER TABLE `".concat(diff.tableName, "` ADD PRIMARY KEY (").concat(indexColumns.join(', '), ");");
3745
3847
  console.log("Executing: ".concat(primaryKeySQL));
3746
- _context.next = 80;
3848
+ _context.next = 116;
3747
3849
  return connection.query(primaryKeySQL);
3748
- case 80:
3749
- _context.next = 83;
3850
+ case 116:
3851
+ _context.next = 119;
3750
3852
  break;
3751
- case 82:
3853
+ case 118:
3752
3854
  logger("Cannot create PRIMARY index on ".concat(diff.tableName, ". ColumnName is undefined or empty."), diff.index);
3753
- case 83:
3754
- _context.next = 94;
3855
+ case 119:
3856
+ _context.next = 130;
3755
3857
  break;
3756
- case 85:
3858
+ case 121:
3757
3859
  // Create the new index with UNIQUE if applicable
3758
3860
  uniqueSQL = diff.index.Unique ? 'UNIQUE ' : ''; // Check if the index is unique
3759
3861
  if (!(indexColumns.length > 0)) {
3760
- _context.next = 93;
3862
+ _context.next = 129;
3761
3863
  break;
3762
3864
  }
3763
3865
  addIndexSQL = "ALTER TABLE `".concat(diff.tableName, "` ADD ").concat(uniqueSQL, "INDEX `").concat(diff.index.Name, "` (").concat(indexColumns.join(', '), ");");
3764
3866
  console.log("Executing: ".concat(addIndexSQL));
3765
- _context.next = 91;
3867
+ _context.next = 127;
3766
3868
  return connection.query(addIndexSQL);
3767
- case 91:
3768
- _context.next = 94;
3869
+ case 127:
3870
+ _context.next = 130;
3769
3871
  break;
3770
- case 93:
3872
+ case 129:
3771
3873
  logger("Cannot create index ".concat(diff.index.Name, " on ").concat(diff.tableName, ". ColumnName is undefined or empty."), diff.index);
3772
- case 94:
3773
- _context.next = 213;
3874
+ case 130:
3875
+ _context.next = 249;
3774
3876
  break;
3775
- case 96:
3877
+ case 132:
3776
3878
  if (!(diff.type === 'extra_index')) {
3777
- _context.next = 110;
3879
+ _context.next = 146;
3778
3880
  break;
3779
3881
  }
3780
3882
  if (!(diff.index.Name === 'PRIMARY')) {
3781
- _context.next = 104;
3883
+ _context.next = 140;
3782
3884
  break;
3783
3885
  }
3784
3886
  dropPrimaryKeySQL = "ALTER TABLE `".concat(diff.tableName, "` DROP PRIMARY KEY;");
3785
3887
  console.log("Executing: ".concat(dropPrimaryKeySQL));
3786
- _context.next = 102;
3888
+ _context.next = 138;
3787
3889
  return connection.query(dropPrimaryKeySQL);
3788
- case 102:
3789
- _context.next = 108;
3890
+ case 138:
3891
+ _context.next = 144;
3790
3892
  break;
3791
- case 104:
3893
+ case 140:
3792
3894
  dropIndexSQL = "DROP INDEX `".concat(diff.index.Name, "` ON `").concat(diff.tableName, "`;");
3793
3895
  console.log("Executing: ".concat(dropIndexSQL));
3794
- _context.next = 108;
3896
+ _context.next = 144;
3795
3897
  return connection.query(dropIndexSQL);
3796
- case 108:
3797
- _context.next = 213;
3898
+ case 144:
3899
+ _context.next = 249;
3798
3900
  break;
3799
- case 110:
3901
+ case 146:
3800
3902
  if (!(diff.type === 'mismatched_index')) {
3801
- _context.next = 138;
3903
+ _context.next = 174;
3802
3904
  break;
3803
3905
  }
3804
3906
  _tableName3 = diff.tableName, index = diff.index; // Drop the existing index
3805
3907
  if (!(index.Name === 'PRIMARY')) {
3806
- _context.next = 119;
3908
+ _context.next = 155;
3807
3909
  break;
3808
3910
  }
3809
3911
  _dropPrimaryKeySQL = "ALTER TABLE `".concat(_tableName3, "` DROP PRIMARY KEY;");
3810
3912
  console.log("Executing: ".concat(_dropPrimaryKeySQL));
3811
- _context.next = 117;
3913
+ _context.next = 153;
3812
3914
  return connection.query(_dropPrimaryKeySQL);
3813
- case 117:
3814
- _context.next = 123;
3915
+ case 153:
3916
+ _context.next = 159;
3815
3917
  break;
3816
- case 119:
3918
+ case 155:
3817
3919
  _dropIndexSQL = "DROP INDEX `".concat(index.Name, "` ON `").concat(_tableName3, "`;");
3818
3920
  console.log("Executing: ".concat(_dropIndexSQL));
3819
- _context.next = 123;
3921
+ _context.next = 159;
3820
3922
  return connection.query(_dropIndexSQL);
3821
- case 123:
3923
+ case 159:
3822
3924
  console.log('index', index);
3823
3925
 
3824
3926
  // Create the new index
3825
3927
  _uniqueSQL = index.Unique ? 'UNIQUE ' : ''; // Check if the index is unique
3826
3928
  if (!(index.Name === 'PRIMARY')) {
3827
- _context.next = 132;
3929
+ _context.next = 168;
3828
3930
  break;
3829
3931
  }
3830
3932
  addPrimaryKeySQL = "ALTER TABLE `".concat(_tableName3, "` ADD PRIMARY KEY (").concat(index.ColumnName.join(', '), ");");
3831
3933
  console.log("Executing: ".concat(addPrimaryKeySQL));
3832
- _context.next = 130;
3934
+ _context.next = 166;
3833
3935
  return connection.query(addPrimaryKeySQL);
3834
- case 130:
3835
- _context.next = 136;
3936
+ case 166:
3937
+ _context.next = 172;
3836
3938
  break;
3837
- case 132:
3939
+ case 168:
3838
3940
  _addIndexSQL = "ALTER TABLE `".concat(_tableName3, "` ADD ").concat(_uniqueSQL, "INDEX `").concat(index.Name, "` (").concat(index.ColumnName.join(', '), ");"); // Include UNIQUE if applicable
3839
3941
  console.log("Executing: ".concat(_addIndexSQL));
3840
- _context.next = 136;
3942
+ _context.next = 172;
3841
3943
  return connection.query(_addIndexSQL);
3842
- case 136:
3843
- _context.next = 213;
3944
+ case 172:
3945
+ _context.next = 249;
3844
3946
  break;
3845
- case 138:
3947
+ case 174:
3846
3948
  if (!(diff.type === 'missing_procedure' || diff.type === 'mismatched_procedure')) {
3847
- _context.next = 149;
3949
+ _context.next = 185;
3848
3950
  break;
3849
3951
  }
3850
3952
  // Drop the procedure if it exists
3851
3953
  dropCurrentProcSQL = "DROP PROCEDURE IF EXISTS `".concat(diff.Name, "`;");
3852
3954
  console.log("Executing: ".concat(dropCurrentProcSQL));
3853
- _context.next = 143;
3955
+ _context.next = 179;
3854
3956
  return connection.query(dropCurrentProcSQL);
3855
- case 143:
3957
+ case 179:
3856
3958
  // Create the procedure
3857
3959
  createProcSQL = diff.Definition;
3858
3960
  console.log("Executing: ".concat(createProcSQL)); // Log the SQL statement
3859
- _context.next = 147;
3961
+ _context.next = 183;
3860
3962
  return connection.query(createProcSQL);
3861
- case 147:
3862
- _context.next = 213;
3963
+ case 183:
3964
+ _context.next = 249;
3863
3965
  break;
3864
- case 149:
3966
+ case 185:
3865
3967
  if (!(diff.type === 'missing_view')) {
3866
- _context.next = 160;
3968
+ _context.next = 196;
3867
3969
  break;
3868
3970
  }
3869
3971
  dropViewSQL = "DROP VIEW IF EXISTS `".concat(diff.viewName, "`;");
3870
3972
  console.log("Executing: ".concat(dropViewSQL));
3871
- _context.next = 154;
3973
+ _context.next = 190;
3872
3974
  return connection.query(dropViewSQL);
3873
- case 154:
3975
+ case 190:
3874
3976
  createViewSQL = diff.definition; // Use the expected definition to create the view
3875
3977
  console.log("Executing: ".concat(createViewSQL));
3876
- _context.next = 158;
3978
+ _context.next = 194;
3877
3979
  return connection.query(createViewSQL);
3878
- case 158:
3879
- _context.next = 213;
3980
+ case 194:
3981
+ _context.next = 249;
3880
3982
  break;
3881
- case 160:
3983
+ case 196:
3882
3984
  if (!(diff.type === 'mismatched_view')) {
3883
- _context.next = 171;
3985
+ _context.next = 207;
3884
3986
  break;
3885
3987
  }
3886
3988
  _dropViewSQL = "DROP VIEW IF EXISTS `".concat(diff.viewName, "`;");
3887
3989
  console.log("Executing: ".concat(_dropViewSQL));
3888
- _context.next = 165;
3990
+ _context.next = 201;
3889
3991
  return connection.query(_dropViewSQL);
3890
- case 165:
3992
+ case 201:
3891
3993
  _createViewSQL = diff.definition; // Use the expected definition to create the view
3892
3994
  console.log("Executing: ".concat(_createViewSQL));
3893
- _context.next = 169;
3995
+ _context.next = 205;
3894
3996
  return connection.query(_createViewSQL);
3895
- case 169:
3896
- _context.next = 213;
3997
+ case 205:
3998
+ _context.next = 249;
3897
3999
  break;
3898
- case 171:
4000
+ case 207:
3899
4001
  if (!(diff.type === 'extra_view')) {
3900
- _context.next = 178;
4002
+ _context.next = 214;
3901
4003
  break;
3902
4004
  }
3903
4005
  _dropViewSQL2 = "DROP VIEW IF EXISTS `".concat(diff.viewName, "`;");
3904
4006
  console.log("Executing: ".concat(_dropViewSQL2));
3905
- _context.next = 176;
4007
+ _context.next = 212;
3906
4008
  return connection.query(_dropViewSQL2);
3907
- case 176:
3908
- _context.next = 213;
4009
+ case 212:
4010
+ _context.next = 249;
3909
4011
  break;
3910
- case 178:
4012
+ case 214:
3911
4013
  if (!(diff.type === 'missing_trigger')) {
3912
- _context.next = 186;
4014
+ _context.next = 222;
3913
4015
  break;
3914
4016
  }
3915
4017
  createTriggerSQL = diff.trigger.Definition;
3916
4018
  logger('fixing trigger', diff.trigger.Name);
3917
4019
  console.log("Executing: ".concat(createTriggerSQL));
3918
- _context.next = 184;
4020
+ _context.next = 220;
3919
4021
  return connection.query(createTriggerSQL);
3920
- case 184:
3921
- _context.next = 213;
4022
+ case 220:
4023
+ _context.next = 249;
3922
4024
  break;
3923
- case 186:
4025
+ case 222:
3924
4026
  if (!(diff.type === 'mismatched_trigger')) {
3925
- _context.next = 197;
4027
+ _context.next = 233;
3926
4028
  break;
3927
4029
  }
3928
4030
  dropTriggerSQL = "DROP TRIGGER IF EXISTS `".concat(diff.trigger.Name, "`;");
3929
4031
  console.log("Executing: ".concat(dropTriggerSQL));
3930
- _context.next = 191;
4032
+ _context.next = 227;
3931
4033
  return connection.query(dropTriggerSQL);
3932
- case 191:
4034
+ case 227:
3933
4035
  _createTriggerSQL = diff.trigger.Definition;
3934
4036
  console.log("Executing: ".concat(_createTriggerSQL));
3935
- _context.next = 195;
4037
+ _context.next = 231;
3936
4038
  return connection.query(_createTriggerSQL);
3937
- case 195:
3938
- _context.next = 213;
4039
+ case 231:
4040
+ _context.next = 249;
3939
4041
  break;
3940
- case 197:
4042
+ case 233:
3941
4043
  if (!(diff.type === 'extra_trigger')) {
3942
- _context.next = 204;
4044
+ _context.next = 240;
3943
4045
  break;
3944
4046
  }
3945
4047
  _dropTriggerSQL = "DROP TRIGGER IF EXISTS `".concat(diff.trigger.Name, "`;");
3946
4048
  console.log("Executing: ".concat(_dropTriggerSQL));
3947
- _context.next = 202;
4049
+ _context.next = 238;
3948
4050
  return connection.query(_dropTriggerSQL);
3949
- case 202:
3950
- _context.next = 213;
4051
+ case 238:
4052
+ _context.next = 249;
3951
4053
  break;
3952
- case 204:
4054
+ case 240:
3953
4055
  if (!(diff.type === 'missing_database')) {
3954
- _context.next = 212;
4056
+ _context.next = 248;
3955
4057
  break;
3956
4058
  }
3957
4059
  logger('fixing missing database', diff.database);
3958
4060
  _createDatabaseSQL = "CREATE DATABASE `".concat(diff.database, "`;");
3959
4061
  console.log("Executing: ".concat(_createDatabaseSQL));
3960
- _context.next = 210;
4062
+ _context.next = 246;
3961
4063
  return connection.query(_createDatabaseSQL);
3962
- case 210:
3963
- _context.next = 213;
4064
+ case 246:
4065
+ _context.next = 249;
3964
4066
  break;
3965
- case 212:
4067
+ case 248:
3966
4068
  logger("Error applying difference of type:".concat(diff.type), diff);
3967
- case 213:
3968
- _context.next = 218;
4069
+ case 249:
4070
+ _context.next = 254;
3969
4071
  break;
3970
- case 215:
3971
- _context.prev = 215;
4072
+ case 251:
4073
+ _context.prev = 251;
3972
4074
  _context.t0 = _context["catch"](1);
3973
4075
  logger("Error applying difference of type:".concat(diff.type, ". Error: ").concat(_context.t0.message), diff);
3974
- case 218:
4076
+ case 254:
3975
4077
  case "end":
3976
4078
  return _context.stop();
3977
4079
  }
3978
- }, _loop, null, [[1, 215]]);
4080
+ }, _loop, null, [[1, 251]]);
3979
4081
  });
3980
4082
  _iterator.s();
3981
4083
  case 5:
package/dist/index.js CHANGED
@@ -134,7 +134,10 @@ function _iterableToArrayLimit(r, l) {
134
134
  f = true,
135
135
  o = false;
136
136
  try {
137
- if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
137
+ if (i = (t = t.call(r)).next, 0 === l) {
138
+ if (Object(t) !== t) return;
139
+ f = !1;
140
+ } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
138
141
  } catch (r) {
139
142
  o = true, n = r;
140
143
  } finally {
@@ -3596,7 +3599,7 @@ var applyDifferences = /*#__PURE__*/function () {
3596
3599
  _iterator = _createForOfIteratorHelper(differences);
3597
3600
  _context2.prev = 2;
3598
3601
  _loop = /*#__PURE__*/_regeneratorRuntime().mark(function _loop() {
3599
- var diff, createDatabaseSQL, tableName, createSQL, _tableName, dropTableSQL, addFieldSQL, modifyFieldSQL, defaultClause, _modifyFieldSQL, dropFieldSQL, _tableName2, field, isTimestamp, _defaultClause, currentField, hasOnUpdate, _dropFieldSQL, existingColumns, columnNames, missingColumns, indexColumns, primaryKeySQL, uniqueSQL, addIndexSQL, dropPrimaryKeySQL, dropIndexSQL, _tableName3, index, _dropPrimaryKeySQL, _dropIndexSQL, _uniqueSQL, addPrimaryKeySQL, _addIndexSQL, dropCurrentProcSQL, createProcSQL, dropViewSQL, createViewSQL, _dropViewSQL, _createViewSQL, _dropViewSQL2, createTriggerSQL, dropTriggerSQL, _createTriggerSQL, _dropTriggerSQL, _createDatabaseSQL;
3602
+ var diff, createDatabaseSQL, tableName, createSQL, _tableName, dropTableSQL, addFieldSQL, modifyFieldSQL, defaultClause, _modifyFieldSQL, dropFieldSQL, _tableName2, field, currentField, needsBatchProcessing, addColumnSQL, totalUpdated, batchSize, batchCount, updateSQL, result, rowsAffected, verifySQL, _yield$connection$que, _yield$connection$que2, verifyResult, swapColumnsSQL, isTimestamp, _defaultClause, _currentField, hasOnUpdate, _modifyFieldSQL2, existingColumns, columnNames, missingColumns, indexColumns, primaryKeySQL, uniqueSQL, addIndexSQL, dropPrimaryKeySQL, dropIndexSQL, _tableName3, index, _dropPrimaryKeySQL, _dropIndexSQL, _uniqueSQL, addPrimaryKeySQL, _addIndexSQL, dropCurrentProcSQL, createProcSQL, dropViewSQL, createViewSQL, _dropViewSQL, _createViewSQL, _dropViewSQL2, createTriggerSQL, dropTriggerSQL, _createTriggerSQL, _dropTriggerSQL, _createDatabaseSQL;
3600
3603
  return _regeneratorRuntime().wrap(function _loop$(_context) {
3601
3604
  while (1) switch (_context.prev = _context.next) {
3602
3605
  case 0:
@@ -3626,7 +3629,7 @@ var applyDifferences = /*#__PURE__*/function () {
3626
3629
  _context.next = 16;
3627
3630
  return connection.query(createSQL);
3628
3631
  case 16:
3629
- _context.next = 213;
3632
+ _context.next = 249;
3630
3633
  break;
3631
3634
  case 18:
3632
3635
  if (!(diff.type === 'extra_table')) {
@@ -3640,7 +3643,7 @@ var applyDifferences = /*#__PURE__*/function () {
3640
3643
  _context.next = 25;
3641
3644
  return connection.query(dropTableSQL);
3642
3645
  case 25:
3643
- _context.next = 213;
3646
+ _context.next = 249;
3644
3647
  break;
3645
3648
  case 27:
3646
3649
  if (!(diff.type === 'missing_field')) {
@@ -3672,7 +3675,7 @@ var applyDifferences = /*#__PURE__*/function () {
3672
3675
  _context.next = 43;
3673
3676
  return connection.query(_modifyFieldSQL);
3674
3677
  case 43:
3675
- _context.next = 213;
3678
+ _context.next = 249;
3676
3679
  break;
3677
3680
  case 45:
3678
3681
  if (!(diff.type === 'extra_field')) {
@@ -3684,33 +3687,132 @@ var applyDifferences = /*#__PURE__*/function () {
3684
3687
  _context.next = 50;
3685
3688
  return connection.query(dropFieldSQL);
3686
3689
  case 50:
3687
- _context.next = 213;
3690
+ _context.next = 249;
3688
3691
  break;
3689
3692
  case 52:
3690
3693
  if (!(diff.type === 'mismatched_field')) {
3691
- _context.next = 64;
3694
+ _context.next = 100;
3692
3695
  break;
3693
3696
  }
3694
- _tableName2 = diff.tableName, field = diff.field;
3697
+ _tableName2 = diff.tableName, field = diff.field, currentField = diff.currentField; // Helper function to determine if we need batch processing
3698
+ needsBatchProcessing = function needsBatchProcessing() {
3699
+ var currentType = currentField.Type.toLowerCase();
3700
+ var newType = field.Type.toLowerCase();
3701
+
3702
+ // Decimal changes
3703
+ if (currentType.includes('decimal') && newType.includes('decimal')) {
3704
+ return true;
3705
+ }
3706
+
3707
+ // VARCHAR/CHAR length reductions
3708
+ if ((currentType.includes('varchar') || currentType.includes('char')) && (newType.includes('varchar') || newType.includes('char'))) {
3709
+ var _currentType$match, _newType$match;
3710
+ var currentLength = parseInt(((_currentType$match = currentType.match(/\((\d+)\)/)) === null || _currentType$match === undefined ? undefined : _currentType$match[1]) || '0');
3711
+ var newLength = parseInt(((_newType$match = newType.match(/\((\d+)\)/)) === null || _newType$match === undefined ? undefined : _newType$match[1]) || '0');
3712
+ return newLength < currentLength;
3713
+ }
3714
+
3715
+ // TEXT to VARCHAR conversions
3716
+ if (currentType.includes('text') && newType.includes('varchar')) {
3717
+ return true;
3718
+ }
3719
+
3720
+ // Numeric type downgrades
3721
+ var numericDowngrades = {
3722
+ 'bigint': ['int', 'mediumint', 'smallint', 'tinyint'],
3723
+ 'int': ['mediumint', 'smallint', 'tinyint'],
3724
+ 'mediumint': ['smallint', 'tinyint'],
3725
+ 'smallint': ['tinyint'],
3726
+ 'double': ['float', 'decimal'],
3727
+ 'float': ['decimal']
3728
+ };
3729
+ for (var _i = 0, _Object$entries = Object.entries(numericDowngrades); _i < _Object$entries.length; _i++) {
3730
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
3731
+ larger = _Object$entries$_i[0],
3732
+ smaller = _Object$entries$_i[1];
3733
+ if (currentType.includes(larger) && smaller.some(function (t) {
3734
+ return newType.includes(t);
3735
+ })) {
3736
+ return true;
3737
+ }
3738
+ }
3739
+ return false;
3740
+ };
3741
+ if (!needsBatchProcessing()) {
3742
+ _context.next = 90;
3743
+ break;
3744
+ }
3745
+ // Step 1: Add new column
3746
+ addColumnSQL = "ALTER TABLE `".concat(_tableName2, "` ADD COLUMN `").concat(field.Field, "_new` ").concat(field.Type, " DEFAULT NULL;");
3747
+ logger("Adding new column for type conversion: ".concat(addColumnSQL));
3748
+ _context.next = 60;
3749
+ return connection.query(addColumnSQL);
3750
+ case 60:
3751
+ // Step 2: Copy data in batches
3752
+ totalUpdated = 0;
3753
+ batchSize = 10000;
3754
+ batchCount = 0;
3755
+ case 63:
3756
+ updateSQL = "\n UPDATE `".concat(_tableName2, "` \n SET `").concat(field.Field, "_new` = `").concat(field.Field, "`\n WHERE `").concat(field.Field, "_new` IS NULL \n AND `").concat(field.Field, "` IS NOT NULL\n LIMIT ").concat(batchSize, ";\n ");
3757
+ _context.next = 67;
3758
+ return connection.query(updateSQL);
3759
+ case 67:
3760
+ result = _context.sent;
3761
+ rowsAffected = result.affectedRows;
3762
+ if (!(rowsAffected === 0)) {
3763
+ _context.next = 71;
3764
+ break;
3765
+ }
3766
+ return _context.abrupt("break", 76);
3767
+ case 71:
3768
+ totalUpdated += rowsAffected;
3769
+ batchCount++;
3770
+ logger("Batch ".concat(batchCount, ": Converted ").concat(rowsAffected, " rows. Total converted: ").concat(totalUpdated));
3771
+ _context.next = 63;
3772
+ break;
3773
+ case 76:
3774
+ // Step 3: Verify data (optional)
3775
+ verifySQL = "\n SELECT COUNT(*) as mismatch FROM `".concat(_tableName2, "`\n WHERE `").concat(field.Field, "` IS NOT NULL \n AND `").concat(field.Field, "_new` IS NULL;\n ");
3776
+ _context.next = 79;
3777
+ return connection.query(verifySQL);
3778
+ case 79:
3779
+ _yield$connection$que = _context.sent;
3780
+ _yield$connection$que2 = _slicedToArray(_yield$connection$que, 1);
3781
+ verifyResult = _yield$connection$que2[0];
3782
+ if (verifyResult.mismatch > 0) {
3783
+ logger("Warning: ".concat(verifyResult.mismatch, " rows may have failed conversion"));
3784
+ }
3785
+
3786
+ // Step 4: Swap columns
3787
+ swapColumnsSQL = "\n ALTER TABLE `".concat(_tableName2, "`\n DROP COLUMN `").concat(field.Field, "`,\n RENAME COLUMN `").concat(field.Field, "_new` TO `").concat(field.Field, "`\n ");
3788
+ logger("Swapping columns: ".concat(swapColumnsSQL));
3789
+ _context.next = 87;
3790
+ return connection.query(swapColumnsSQL);
3791
+ case 87:
3792
+ logger("Completed type conversion for ".concat(field.Field, ". Total rows converted: ").concat(totalUpdated));
3793
+ _context.next = 98;
3794
+ break;
3795
+ case 90:
3796
+ // Original mismatched field handling for simple changes
3695
3797
  isTimestamp = field.Type.toLowerCase().includes('timestamp'); // Handle timestamp specific attributes
3696
3798
  _defaultClause = field.Default === 'CURRENT_TIMESTAMP' ? 'DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' : field.Default === 'NULL' ? isTimestamp ? 'NULL DEFAULT NULL' : 'DEFAULT NULL' : field.Default !== undefined && field.Default !== null ? "DEFAULT '".concat(field.Default, "'") : isTimestamp ? 'NULL DEFAULT NULL' : 'DEFAULT NULL'; // Remove ON UPDATE CURRENT_TIMESTAMP if we're changing away from CURRENT_TIMESTAMP
3697
- currentField = diff.currentField;
3698
- hasOnUpdate = (currentField === null || currentField === undefined ? undefined : currentField.Default) === 'CURRENT_TIMESTAMP' && field.Default !== 'CURRENT_TIMESTAMP';
3699
- _dropFieldSQL = "ALTER TABLE `".concat(_tableName2, "` CHANGE `").concat(field.Field, "` `").concat(field.Field, "` ").concat(field.Type, " ").concat(field.NotNull ? 'NOT NULL' : '', " ").concat(_defaultClause).concat(hasOnUpdate ? ' ON UPDATE CURRENT_TIMESTAMP' : '', ";");
3700
- logger("Executing: ".concat(_dropFieldSQL));
3701
- _context.next = 62;
3702
- return connection.query(_dropFieldSQL);
3703
- case 62:
3704
- _context.next = 213;
3799
+ _currentField = diff.currentField;
3800
+ hasOnUpdate = (_currentField === null || _currentField === undefined ? undefined : _currentField.Default) === 'CURRENT_TIMESTAMP' && field.Default !== 'CURRENT_TIMESTAMP';
3801
+ _modifyFieldSQL2 = "ALTER TABLE `".concat(_tableName2, "` CHANGE `").concat(field.Field, "` `").concat(field.Field, "` ").concat(field.Type, " ").concat(field.NotNull ? 'NOT NULL' : '', " ").concat(_defaultClause).concat(hasOnUpdate ? ' ON UPDATE CURRENT_TIMESTAMP' : '', ";");
3802
+ console.log("Executing: ".concat(_modifyFieldSQL2));
3803
+ _context.next = 98;
3804
+ return connection.query(_modifyFieldSQL2);
3805
+ case 98:
3806
+ _context.next = 249;
3705
3807
  break;
3706
- case 64:
3808
+ case 100:
3707
3809
  if (!(diff.type === 'missing_index')) {
3708
- _context.next = 96;
3810
+ _context.next = 132;
3709
3811
  break;
3710
3812
  }
3711
- _context.next = 67;
3813
+ _context.next = 103;
3712
3814
  return connection.query("SHOW COLUMNS FROM `".concat(diff.tableName, "`"));
3713
- case 67:
3815
+ case 103:
3714
3816
  existingColumns = _context.sent;
3715
3817
  columnNames = existingColumns.map(function (col) {
3716
3818
  return col.Field;
@@ -3719,260 +3821,260 @@ var applyDifferences = /*#__PURE__*/function () {
3719
3821
  return !columnNames.includes(col);
3720
3822
  }) : [];
3721
3823
  if (!(missingColumns.length > 0)) {
3722
- _context.next = 73;
3824
+ _context.next = 109;
3723
3825
  break;
3724
3826
  }
3725
3827
  logger("Cannot create index ".concat(diff.index.Name, " on ").concat(diff.tableName, ". Missing columns: ").concat(missingColumns.join(', ')));
3726
3828
  return _context.abrupt("return", 0);
3727
- case 73:
3829
+ case 109:
3728
3830
  // Prepare the index columns with proper formatting
3729
3831
  indexColumns = diff.index.ColumnName.map(function (col) {
3730
3832
  return "`".concat(col, "`");
3731
3833
  }); // Format column names
3732
3834
  // Handle PRIMARY index creation
3733
3835
  if (!(diff.index.Name === 'PRIMARY')) {
3734
- _context.next = 85;
3836
+ _context.next = 121;
3735
3837
  break;
3736
3838
  }
3737
3839
  if (!(indexColumns.length > 0)) {
3738
- _context.next = 82;
3840
+ _context.next = 118;
3739
3841
  break;
3740
3842
  }
3741
3843
  primaryKeySQL = "ALTER TABLE `".concat(diff.tableName, "` ADD PRIMARY KEY (").concat(indexColumns.join(', '), ");");
3742
3844
  console.log("Executing: ".concat(primaryKeySQL));
3743
- _context.next = 80;
3845
+ _context.next = 116;
3744
3846
  return connection.query(primaryKeySQL);
3745
- case 80:
3746
- _context.next = 83;
3847
+ case 116:
3848
+ _context.next = 119;
3747
3849
  break;
3748
- case 82:
3850
+ case 118:
3749
3851
  logger("Cannot create PRIMARY index on ".concat(diff.tableName, ". ColumnName is undefined or empty."), diff.index);
3750
- case 83:
3751
- _context.next = 94;
3852
+ case 119:
3853
+ _context.next = 130;
3752
3854
  break;
3753
- case 85:
3855
+ case 121:
3754
3856
  // Create the new index with UNIQUE if applicable
3755
3857
  uniqueSQL = diff.index.Unique ? 'UNIQUE ' : ''; // Check if the index is unique
3756
3858
  if (!(indexColumns.length > 0)) {
3757
- _context.next = 93;
3859
+ _context.next = 129;
3758
3860
  break;
3759
3861
  }
3760
3862
  addIndexSQL = "ALTER TABLE `".concat(diff.tableName, "` ADD ").concat(uniqueSQL, "INDEX `").concat(diff.index.Name, "` (").concat(indexColumns.join(', '), ");");
3761
3863
  console.log("Executing: ".concat(addIndexSQL));
3762
- _context.next = 91;
3864
+ _context.next = 127;
3763
3865
  return connection.query(addIndexSQL);
3764
- case 91:
3765
- _context.next = 94;
3866
+ case 127:
3867
+ _context.next = 130;
3766
3868
  break;
3767
- case 93:
3869
+ case 129:
3768
3870
  logger("Cannot create index ".concat(diff.index.Name, " on ").concat(diff.tableName, ". ColumnName is undefined or empty."), diff.index);
3769
- case 94:
3770
- _context.next = 213;
3871
+ case 130:
3872
+ _context.next = 249;
3771
3873
  break;
3772
- case 96:
3874
+ case 132:
3773
3875
  if (!(diff.type === 'extra_index')) {
3774
- _context.next = 110;
3876
+ _context.next = 146;
3775
3877
  break;
3776
3878
  }
3777
3879
  if (!(diff.index.Name === 'PRIMARY')) {
3778
- _context.next = 104;
3880
+ _context.next = 140;
3779
3881
  break;
3780
3882
  }
3781
3883
  dropPrimaryKeySQL = "ALTER TABLE `".concat(diff.tableName, "` DROP PRIMARY KEY;");
3782
3884
  console.log("Executing: ".concat(dropPrimaryKeySQL));
3783
- _context.next = 102;
3885
+ _context.next = 138;
3784
3886
  return connection.query(dropPrimaryKeySQL);
3785
- case 102:
3786
- _context.next = 108;
3887
+ case 138:
3888
+ _context.next = 144;
3787
3889
  break;
3788
- case 104:
3890
+ case 140:
3789
3891
  dropIndexSQL = "DROP INDEX `".concat(diff.index.Name, "` ON `").concat(diff.tableName, "`;");
3790
3892
  console.log("Executing: ".concat(dropIndexSQL));
3791
- _context.next = 108;
3893
+ _context.next = 144;
3792
3894
  return connection.query(dropIndexSQL);
3793
- case 108:
3794
- _context.next = 213;
3895
+ case 144:
3896
+ _context.next = 249;
3795
3897
  break;
3796
- case 110:
3898
+ case 146:
3797
3899
  if (!(diff.type === 'mismatched_index')) {
3798
- _context.next = 138;
3900
+ _context.next = 174;
3799
3901
  break;
3800
3902
  }
3801
3903
  _tableName3 = diff.tableName, index = diff.index; // Drop the existing index
3802
3904
  if (!(index.Name === 'PRIMARY')) {
3803
- _context.next = 119;
3905
+ _context.next = 155;
3804
3906
  break;
3805
3907
  }
3806
3908
  _dropPrimaryKeySQL = "ALTER TABLE `".concat(_tableName3, "` DROP PRIMARY KEY;");
3807
3909
  console.log("Executing: ".concat(_dropPrimaryKeySQL));
3808
- _context.next = 117;
3910
+ _context.next = 153;
3809
3911
  return connection.query(_dropPrimaryKeySQL);
3810
- case 117:
3811
- _context.next = 123;
3912
+ case 153:
3913
+ _context.next = 159;
3812
3914
  break;
3813
- case 119:
3915
+ case 155:
3814
3916
  _dropIndexSQL = "DROP INDEX `".concat(index.Name, "` ON `").concat(_tableName3, "`;");
3815
3917
  console.log("Executing: ".concat(_dropIndexSQL));
3816
- _context.next = 123;
3918
+ _context.next = 159;
3817
3919
  return connection.query(_dropIndexSQL);
3818
- case 123:
3920
+ case 159:
3819
3921
  console.log('index', index);
3820
3922
 
3821
3923
  // Create the new index
3822
3924
  _uniqueSQL = index.Unique ? 'UNIQUE ' : ''; // Check if the index is unique
3823
3925
  if (!(index.Name === 'PRIMARY')) {
3824
- _context.next = 132;
3926
+ _context.next = 168;
3825
3927
  break;
3826
3928
  }
3827
3929
  addPrimaryKeySQL = "ALTER TABLE `".concat(_tableName3, "` ADD PRIMARY KEY (").concat(index.ColumnName.join(', '), ");");
3828
3930
  console.log("Executing: ".concat(addPrimaryKeySQL));
3829
- _context.next = 130;
3931
+ _context.next = 166;
3830
3932
  return connection.query(addPrimaryKeySQL);
3831
- case 130:
3832
- _context.next = 136;
3933
+ case 166:
3934
+ _context.next = 172;
3833
3935
  break;
3834
- case 132:
3936
+ case 168:
3835
3937
  _addIndexSQL = "ALTER TABLE `".concat(_tableName3, "` ADD ").concat(_uniqueSQL, "INDEX `").concat(index.Name, "` (").concat(index.ColumnName.join(', '), ");"); // Include UNIQUE if applicable
3836
3938
  console.log("Executing: ".concat(_addIndexSQL));
3837
- _context.next = 136;
3939
+ _context.next = 172;
3838
3940
  return connection.query(_addIndexSQL);
3839
- case 136:
3840
- _context.next = 213;
3941
+ case 172:
3942
+ _context.next = 249;
3841
3943
  break;
3842
- case 138:
3944
+ case 174:
3843
3945
  if (!(diff.type === 'missing_procedure' || diff.type === 'mismatched_procedure')) {
3844
- _context.next = 149;
3946
+ _context.next = 185;
3845
3947
  break;
3846
3948
  }
3847
3949
  // Drop the procedure if it exists
3848
3950
  dropCurrentProcSQL = "DROP PROCEDURE IF EXISTS `".concat(diff.Name, "`;");
3849
3951
  console.log("Executing: ".concat(dropCurrentProcSQL));
3850
- _context.next = 143;
3952
+ _context.next = 179;
3851
3953
  return connection.query(dropCurrentProcSQL);
3852
- case 143:
3954
+ case 179:
3853
3955
  // Create the procedure
3854
3956
  createProcSQL = diff.Definition;
3855
3957
  console.log("Executing: ".concat(createProcSQL)); // Log the SQL statement
3856
- _context.next = 147;
3958
+ _context.next = 183;
3857
3959
  return connection.query(createProcSQL);
3858
- case 147:
3859
- _context.next = 213;
3960
+ case 183:
3961
+ _context.next = 249;
3860
3962
  break;
3861
- case 149:
3963
+ case 185:
3862
3964
  if (!(diff.type === 'missing_view')) {
3863
- _context.next = 160;
3965
+ _context.next = 196;
3864
3966
  break;
3865
3967
  }
3866
3968
  dropViewSQL = "DROP VIEW IF EXISTS `".concat(diff.viewName, "`;");
3867
3969
  console.log("Executing: ".concat(dropViewSQL));
3868
- _context.next = 154;
3970
+ _context.next = 190;
3869
3971
  return connection.query(dropViewSQL);
3870
- case 154:
3972
+ case 190:
3871
3973
  createViewSQL = diff.definition; // Use the expected definition to create the view
3872
3974
  console.log("Executing: ".concat(createViewSQL));
3873
- _context.next = 158;
3975
+ _context.next = 194;
3874
3976
  return connection.query(createViewSQL);
3875
- case 158:
3876
- _context.next = 213;
3977
+ case 194:
3978
+ _context.next = 249;
3877
3979
  break;
3878
- case 160:
3980
+ case 196:
3879
3981
  if (!(diff.type === 'mismatched_view')) {
3880
- _context.next = 171;
3982
+ _context.next = 207;
3881
3983
  break;
3882
3984
  }
3883
3985
  _dropViewSQL = "DROP VIEW IF EXISTS `".concat(diff.viewName, "`;");
3884
3986
  console.log("Executing: ".concat(_dropViewSQL));
3885
- _context.next = 165;
3987
+ _context.next = 201;
3886
3988
  return connection.query(_dropViewSQL);
3887
- case 165:
3989
+ case 201:
3888
3990
  _createViewSQL = diff.definition; // Use the expected definition to create the view
3889
3991
  console.log("Executing: ".concat(_createViewSQL));
3890
- _context.next = 169;
3992
+ _context.next = 205;
3891
3993
  return connection.query(_createViewSQL);
3892
- case 169:
3893
- _context.next = 213;
3994
+ case 205:
3995
+ _context.next = 249;
3894
3996
  break;
3895
- case 171:
3997
+ case 207:
3896
3998
  if (!(diff.type === 'extra_view')) {
3897
- _context.next = 178;
3999
+ _context.next = 214;
3898
4000
  break;
3899
4001
  }
3900
4002
  _dropViewSQL2 = "DROP VIEW IF EXISTS `".concat(diff.viewName, "`;");
3901
4003
  console.log("Executing: ".concat(_dropViewSQL2));
3902
- _context.next = 176;
4004
+ _context.next = 212;
3903
4005
  return connection.query(_dropViewSQL2);
3904
- case 176:
3905
- _context.next = 213;
4006
+ case 212:
4007
+ _context.next = 249;
3906
4008
  break;
3907
- case 178:
4009
+ case 214:
3908
4010
  if (!(diff.type === 'missing_trigger')) {
3909
- _context.next = 186;
4011
+ _context.next = 222;
3910
4012
  break;
3911
4013
  }
3912
4014
  createTriggerSQL = diff.trigger.Definition;
3913
4015
  logger('fixing trigger', diff.trigger.Name);
3914
4016
  console.log("Executing: ".concat(createTriggerSQL));
3915
- _context.next = 184;
4017
+ _context.next = 220;
3916
4018
  return connection.query(createTriggerSQL);
3917
- case 184:
3918
- _context.next = 213;
4019
+ case 220:
4020
+ _context.next = 249;
3919
4021
  break;
3920
- case 186:
4022
+ case 222:
3921
4023
  if (!(diff.type === 'mismatched_trigger')) {
3922
- _context.next = 197;
4024
+ _context.next = 233;
3923
4025
  break;
3924
4026
  }
3925
4027
  dropTriggerSQL = "DROP TRIGGER IF EXISTS `".concat(diff.trigger.Name, "`;");
3926
4028
  console.log("Executing: ".concat(dropTriggerSQL));
3927
- _context.next = 191;
4029
+ _context.next = 227;
3928
4030
  return connection.query(dropTriggerSQL);
3929
- case 191:
4031
+ case 227:
3930
4032
  _createTriggerSQL = diff.trigger.Definition;
3931
4033
  console.log("Executing: ".concat(_createTriggerSQL));
3932
- _context.next = 195;
4034
+ _context.next = 231;
3933
4035
  return connection.query(_createTriggerSQL);
3934
- case 195:
3935
- _context.next = 213;
4036
+ case 231:
4037
+ _context.next = 249;
3936
4038
  break;
3937
- case 197:
4039
+ case 233:
3938
4040
  if (!(diff.type === 'extra_trigger')) {
3939
- _context.next = 204;
4041
+ _context.next = 240;
3940
4042
  break;
3941
4043
  }
3942
4044
  _dropTriggerSQL = "DROP TRIGGER IF EXISTS `".concat(diff.trigger.Name, "`;");
3943
4045
  console.log("Executing: ".concat(_dropTriggerSQL));
3944
- _context.next = 202;
4046
+ _context.next = 238;
3945
4047
  return connection.query(_dropTriggerSQL);
3946
- case 202:
3947
- _context.next = 213;
4048
+ case 238:
4049
+ _context.next = 249;
3948
4050
  break;
3949
- case 204:
4051
+ case 240:
3950
4052
  if (!(diff.type === 'missing_database')) {
3951
- _context.next = 212;
4053
+ _context.next = 248;
3952
4054
  break;
3953
4055
  }
3954
4056
  logger('fixing missing database', diff.database);
3955
4057
  _createDatabaseSQL = "CREATE DATABASE `".concat(diff.database, "`;");
3956
4058
  console.log("Executing: ".concat(_createDatabaseSQL));
3957
- _context.next = 210;
4059
+ _context.next = 246;
3958
4060
  return connection.query(_createDatabaseSQL);
3959
- case 210:
3960
- _context.next = 213;
4061
+ case 246:
4062
+ _context.next = 249;
3961
4063
  break;
3962
- case 212:
4064
+ case 248:
3963
4065
  logger("Error applying difference of type:".concat(diff.type), diff);
3964
- case 213:
3965
- _context.next = 218;
4066
+ case 249:
4067
+ _context.next = 254;
3966
4068
  break;
3967
- case 215:
3968
- _context.prev = 215;
4069
+ case 251:
4070
+ _context.prev = 251;
3969
4071
  _context.t0 = _context["catch"](1);
3970
4072
  logger("Error applying difference of type:".concat(diff.type, ". Error: ").concat(_context.t0.message), diff);
3971
- case 218:
4073
+ case 254:
3972
4074
  case "end":
3973
4075
  return _context.stop();
3974
4076
  }
3975
- }, _loop, null, [[1, 215]]);
4077
+ }, _loop, null, [[1, 251]]);
3976
4078
  });
3977
4079
  _iterator.s();
3978
4080
  case 5:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mysqlsyncr",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",
package/src/apply.js CHANGED
@@ -82,26 +82,122 @@ export const applyDifferences = async (connection, database, differences) => {
82
82
  await connection.query(dropFieldSQL);
83
83
 
84
84
  } else if (diff.type === 'mismatched_field') {
85
- const { tableName, field } = diff;
86
- const isTimestamp = field.Type.toLowerCase().includes('timestamp');
85
+ const { tableName, field, currentField } = diff;
87
86
 
88
- // Handle timestamp specific attributes
89
- const defaultClause = field.Default === 'CURRENT_TIMESTAMP'
90
- ? 'DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
91
- : field.Default === 'NULL'
92
- ? isTimestamp ? 'NULL DEFAULT NULL' : 'DEFAULT NULL'
93
- : field.Default !== undefined && field.Default !== null
94
- ? `DEFAULT '${field.Default}'`
95
- : isTimestamp ? 'NULL DEFAULT NULL' : 'DEFAULT NULL';
96
-
97
- // Remove ON UPDATE CURRENT_TIMESTAMP if we're changing away from CURRENT_TIMESTAMP
98
- const currentField = diff.currentField;
99
- const hasOnUpdate = currentField?.Default === 'CURRENT_TIMESTAMP' &&
100
- field.Default !== 'CURRENT_TIMESTAMP';
101
-
102
- const dropFieldSQL = `ALTER TABLE \`${tableName}\` CHANGE \`${field.Field}\` \`${field.Field}\` ${field.Type} ${field.NotNull ? 'NOT NULL' : ''} ${defaultClause}${hasOnUpdate ? ' ON UPDATE CURRENT_TIMESTAMP' : ''};`;
103
- logger(`Executing: ${dropFieldSQL}`);
104
- await connection.query(dropFieldSQL);
87
+ // Helper function to determine if we need batch processing
88
+ const needsBatchProcessing = () => {
89
+ const currentType = currentField.Type.toLowerCase();
90
+ const newType = field.Type.toLowerCase();
91
+
92
+ // Decimal changes
93
+ if (currentType.includes('decimal') && newType.includes('decimal')) {
94
+ return true;
95
+ }
96
+
97
+ // VARCHAR/CHAR length reductions
98
+ if ((currentType.includes('varchar') || currentType.includes('char')) &&
99
+ (newType.includes('varchar') || newType.includes('char'))) {
100
+ const currentLength = parseInt(currentType.match(/\((\d+)\)/)?.[1] || '0');
101
+ const newLength = parseInt(newType.match(/\((\d+)\)/)?.[1] || '0');
102
+ return newLength < currentLength;
103
+ }
104
+
105
+ // TEXT to VARCHAR conversions
106
+ if (currentType.includes('text') && newType.includes('varchar')) {
107
+ return true;
108
+ }
109
+
110
+ // Numeric type downgrades
111
+ const numericDowngrades = {
112
+ 'bigint': ['int', 'mediumint', 'smallint', 'tinyint'],
113
+ 'int': ['mediumint', 'smallint', 'tinyint'],
114
+ 'mediumint': ['smallint', 'tinyint'],
115
+ 'smallint': ['tinyint'],
116
+ 'double': ['float', 'decimal'],
117
+ 'float': ['decimal']
118
+ };
119
+
120
+ for (const [larger, smaller] of Object.entries(numericDowngrades)) {
121
+ if (currentType.includes(larger) && smaller.some(t => newType.includes(t))) {
122
+ return true;
123
+ }
124
+ }
125
+
126
+ return false;
127
+ };
128
+
129
+ if (needsBatchProcessing()) {
130
+ // Step 1: Add new column
131
+ const addColumnSQL = `ALTER TABLE \`${tableName}\` ADD COLUMN \`${field.Field}_new\` ${field.Type} DEFAULT NULL;`;
132
+ logger(`Adding new column for type conversion: ${addColumnSQL}`);
133
+ await connection.query(addColumnSQL);
134
+
135
+ // Step 2: Copy data in batches
136
+ let totalUpdated = 0;
137
+ let batchSize = 10000;
138
+ let batchCount = 0;
139
+
140
+ while (true) {
141
+ const updateSQL = `
142
+ UPDATE \`${tableName}\`
143
+ SET \`${field.Field}_new\` = \`${field.Field}\`
144
+ WHERE \`${field.Field}_new\` IS NULL
145
+ AND \`${field.Field}\` IS NOT NULL
146
+ LIMIT ${batchSize};
147
+ `;
148
+ const result = await connection.query(updateSQL);
149
+ const rowsAffected = result.affectedRows;
150
+
151
+ if (rowsAffected === 0) break;
152
+
153
+ totalUpdated += rowsAffected;
154
+ batchCount++;
155
+ logger(`Batch ${batchCount}: Converted ${rowsAffected} rows. Total converted: ${totalUpdated}`);
156
+ }
157
+
158
+ // Step 3: Verify data (optional)
159
+ const verifySQL = `
160
+ SELECT COUNT(*) as mismatch FROM \`${tableName}\`
161
+ WHERE \`${field.Field}\` IS NOT NULL
162
+ AND \`${field.Field}_new\` IS NULL;
163
+ `;
164
+ const [verifyResult] = await connection.query(verifySQL);
165
+ if (verifyResult.mismatch > 0) {
166
+ logger(`Warning: ${verifyResult.mismatch} rows may have failed conversion`);
167
+ }
168
+
169
+ // Step 4: Swap columns
170
+ const swapColumnsSQL = `
171
+ ALTER TABLE \`${tableName}\`
172
+ DROP COLUMN \`${field.Field}\`,
173
+ RENAME COLUMN \`${field.Field}_new\` TO \`${field.Field}\`
174
+ `;
175
+ logger(`Swapping columns: ${swapColumnsSQL}`);
176
+ await connection.query(swapColumnsSQL);
177
+
178
+ logger(`Completed type conversion for ${field.Field}. Total rows converted: ${totalUpdated}`);
179
+ } else {
180
+ // Original mismatched field handling for simple changes
181
+ const isTimestamp = field.Type.toLowerCase().includes('timestamp');
182
+
183
+ // Handle timestamp specific attributes
184
+ const defaultClause = field.Default === 'CURRENT_TIMESTAMP'
185
+ ? 'DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
186
+ : field.Default === 'NULL'
187
+ ? isTimestamp ? 'NULL DEFAULT NULL' : 'DEFAULT NULL'
188
+ : field.Default !== undefined && field.Default !== null
189
+ ? `DEFAULT '${field.Default}'`
190
+ : isTimestamp ? 'NULL DEFAULT NULL' : 'DEFAULT NULL';
191
+
192
+ // Remove ON UPDATE CURRENT_TIMESTAMP if we're changing away from CURRENT_TIMESTAMP
193
+ const currentField = diff.currentField;
194
+ const hasOnUpdate = currentField?.Default === 'CURRENT_TIMESTAMP' &&
195
+ field.Default !== 'CURRENT_TIMESTAMP';
196
+
197
+ const modifyFieldSQL = `ALTER TABLE \`${tableName}\` CHANGE \`${field.Field}\` \`${field.Field}\` ${field.Type} ${field.NotNull ? 'NOT NULL' : ''} ${defaultClause}${hasOnUpdate ? ' ON UPDATE CURRENT_TIMESTAMP' : ''};`;
198
+ console.log(`Executing: ${modifyFieldSQL}`);
199
+ await connection.query(modifyFieldSQL);
200
+ }
105
201
 
106
202
  } else if (diff.type === 'missing_index') {
107
203
  const existingColumns = await connection.query(`SHOW COLUMNS FROM \`${diff.tableName}\``);