@vlasky/zongji 0.5.8 → 0.6.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.
Files changed (45) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/.mcp.json +17 -0
  3. package/.tap/processinfo/84de694d-068b-4ac7-b4d8-f835c2122b2d.json +892 -0
  4. package/.tap/processinfo/a1e9627e-66eb-412c-aaa5-079a2eb9f385.json +891 -0
  5. package/.tap/processinfo/abc34c6f-f4aa-4405-8f9a-947048be1df2.json +892 -0
  6. package/.tap/processinfo/affe6c5b-a34d-41b9-bb73-4b14d35138de.json +891 -0
  7. package/.tap/processinfo/bf82449d-cdeb-478b-9506-b939a9506bf2.json +883 -0
  8. package/.tap/processinfo/dd98c508-7067-4342-bdf5-63664b630701.json +892 -0
  9. package/.tap/test-results/test_await_end.js.tap +19 -0
  10. package/.tap/test-results/test_await_end2.js.tap +27 -0
  11. package/.tap/test-results/test_debug_init.js.tap +23 -0
  12. package/.tap/test-results/test_debug_init2.js.tap +35 -0
  13. package/.tap/test-results/test_invalid_host.js.tap +34 -0
  14. package/.tap/test-results/test_minimal.js.tap +26 -0
  15. package/.tap/test-results/test_queryseq.js.tap +14 -0
  16. package/.tap/test-results/test_timing.js.tap +17 -0
  17. package/.tap/test-results/test_unref.js.tap +15 -0
  18. package/CHANGELOG.md +55 -0
  19. package/CLAUDE.md +99 -0
  20. package/GTID.md +54 -0
  21. package/README.md +44 -21
  22. package/TODO.md +43 -0
  23. package/docker-compose.yml +14 -13
  24. package/docker-test.sh +7 -7
  25. package/eslint.config.js +35 -0
  26. package/example.js +1 -1
  27. package/index.d.ts +314 -0
  28. package/index.js +444 -272
  29. package/jsconfig.json +15 -0
  30. package/lib/binlog_event.js +296 -217
  31. package/lib/code_map.js +7 -4
  32. package/lib/common.js +46 -24
  33. package/lib/datetime_decode.js +147 -34
  34. package/lib/json_decode.js +12 -6
  35. package/lib/packet/binlog.js +3 -3
  36. package/lib/packet/combinlog.js +22 -20
  37. package/lib/packet/index.js +50 -50
  38. package/lib/reader.js +215 -109
  39. package/lib/rows_event.js +98 -102
  40. package/lib/sequence/binlog.js +142 -39
  41. package/notes.md +77 -0
  42. package/package.json +17 -11
  43. package/scripts/start-mysql.sh +28 -0
  44. package/workerthreads.md +173 -0
  45. package/.eslintrc +0 -19
package/lib/reader.js CHANGED
@@ -1,141 +1,247 @@
1
1
  // Constants for variable length encoded binary
2
- const NULL_COLUMN = 251;
3
- const UNSIGNED_CHAR_COLUMN = 251;
4
- const UNSIGNED_SHORT_COLUMN = 252;
5
- const UNSIGNED_INT24_COLUMN = 253;
6
- const UNSIGNED_INT64_COLUMN = 254;
7
-
8
- function BufferReader(buffer) {
9
- this.buffer = buffer;
10
- this.position = 0;
11
- }
2
+ export const NULL_COLUMN = 251;
3
+ export const UNSIGNED_CHAR_COLUMN = 251;
4
+ export const UNSIGNED_SHORT_COLUMN = 252;
5
+ export const UNSIGNED_INT24_COLUMN = 253;
6
+ export const UNSIGNED_INT64_COLUMN = 254;
12
7
 
13
- BufferReader.prototype.readUInt8 = function() {
14
- const pos = this.position;
15
- this.position += 1;
8
+ const padWith = function(val, length) {
9
+ const bits = val.split('');
10
+ if (bits.length < length) {
11
+ const left = length - bits.length;
12
+ for (let j = left - 1; j >= 0; j--) {
13
+ bits.unshift('0');
14
+ }
15
+ val = bits.join('');
16
+ }
16
17
 
17
- return this.buffer.readUInt8(pos);
18
+ return val;
18
19
  };
19
20
 
20
- BufferReader.prototype.readUInt16 = function() {
21
- const pos = this.position;
22
- this.position += 2;
21
+ class BufferReader {
22
+ constructor(buffer) {
23
+ this.buffer = buffer;
24
+ this.position = 0;
25
+ }
23
26
 
24
- return this.buffer.readUInt16LE(pos);
25
- };
27
+ readUInt8() {
28
+ const pos = this.position;
29
+ this.position += 1;
26
30
 
27
- BufferReader.prototype.readUInt32 = function() {
28
- const pos = this.position;
29
- this.position += 4;
31
+ return this.buffer.readUInt8(pos);
32
+ }
30
33
 
31
- return this.buffer.readUInt32LE(pos);
32
- };
34
+ readUInt16() {
35
+ const pos = this.position;
36
+ this.position += 2;
33
37
 
34
- BufferReader.prototype.readUInt24 = function() {
35
- const low = this.readUInt16();
36
- const high = this.readUInt8();
37
- return (high << 16) + low;
38
- };
38
+ return this.buffer.readUInt16LE(pos);
39
+ }
39
40
 
40
- BufferReader.prototype.readUInt64 = function() {
41
- const pos = this.position;
42
- this.position += 8;
41
+ readUInt32() {
42
+ const pos = this.position;
43
+ this.position += 4;
43
44
 
44
- // from http://stackoverflow.com/questions/17687307/convert-a-64bit-little-endian-integer-to-number
45
- return this.buffer.readInt32LE(pos) +
46
- 0x100000000 * this.buffer.readUInt32LE(pos + 4);
47
- };
45
+ return this.buffer.readUInt32LE(pos);
46
+ }
48
47
 
49
- BufferReader.prototype.readString = function() {
50
- const strBuf = this.buffer.slice(this.position);
51
- this.position = this.buffer.length;
48
+ readUInt24() {
49
+ const low = this.readUInt16();
50
+ const high = this.readUInt8();
51
+ return (high << 16) + low;
52
+ }
52
53
 
53
- return strBuf.toString('ascii');
54
- };
54
+ readUInt64() {
55
+ const pos = this.position;
56
+ this.position += 8;
55
57
 
56
- BufferReader.prototype.readStringInBytes = function(length) {
57
- const strBuf = this.buffer.slice(this.position, this.position + length);
58
- this.position += length;
58
+ // from http://stackoverflow.com/questions/17687307/convert-a-64bit-little-endian-integer-to-number
59
+ return this.buffer.readInt32LE(pos) +
60
+ 0x100000000 * this.buffer.readUInt32LE(pos + 4);
61
+ }
59
62
 
60
- return strBuf.toString('ascii');
61
- };
63
+ readString() {
64
+ const strBuf = this.buffer.slice(this.position);
65
+ this.position = this.buffer.length;
62
66
 
63
- BufferReader.prototype.readHexInBytes = function(length) {
64
- const buf = this.buffer.slice(this.position, this.position + length);
65
- this.position += length;
67
+ return strBuf.toString('ascii');
68
+ }
66
69
 
67
- return buf.toString('hex');
68
- };
70
+ readStringInBytes(length) {
71
+ const strBuf = this.buffer.slice(this.position, this.position + length);
72
+ this.position += length;
69
73
 
70
- BufferReader.prototype.readBytesArray = function(length) {
71
- const result = [];
72
- const hexString = this.readHexInBytes(length);
73
- for (let i = 0; i < hexString.length; i = i + 2) {
74
- result.push(parseInt(hexString.substr(i, 2), 16));
74
+ return strBuf.toString('ascii');
75
75
  }
76
- return result;
77
- };
78
76
 
79
- // Read a variable-length "Length Coded Binary" integer. This is derived
80
- // from the MySQL protocol, and re-used in the binary log format. This
81
- // format uses the first byte to alternately store the actual value for
82
- // integer values <= 250, or to encode the number of following bytes
83
- // used to store the actual value, which can be 2, 3, or 8. It also
84
- // includes support for SQL NULL as a special case.
85
- BufferReader.prototype.readVariant = function() {
86
- let result = null;
87
- const firstByte = this.readUInt8();
88
-
89
- if (firstByte < UNSIGNED_CHAR_COLUMN) {
90
- result = firstByte;
91
- } else if (firstByte === NULL_COLUMN) {
92
- result = null;
93
- } else if (firstByte === UNSIGNED_SHORT_COLUMN) {
94
- result = this.readUInt16();
95
- } else if (firstByte === UNSIGNED_INT24_COLUMN) {
96
- result = this.readUInt24();
97
- } else if (firstByte === UNSIGNED_INT64_COLUMN) {
98
- result = this.readUInt64();
99
- } else {
100
- throw new Error('Invalid variable-length integer');
101
- }
102
-
103
- return result;
104
- };
77
+ readHexInBytes(length) {
78
+ const buf = this.buffer.slice(this.position, this.position + length);
79
+ this.position += length;
105
80
 
106
- const padWith = function(val, length) {
107
- const bits = val.split('');
108
- if (bits.length < length) {
109
- const left = length - bits.length;
110
- for (let j = left - 1; j >= 0; j--) {
111
- bits.unshift('0');
81
+ return buf.toString('hex');
82
+ }
83
+
84
+ readBytesArray(length) {
85
+ const result = [];
86
+ const hexString = this.readHexInBytes(length);
87
+ for (let i = 0; i < hexString.length; i = i + 2) {
88
+ result.push(parseInt(hexString.substr(i, 2), 16));
112
89
  }
113
- val = bits.join('');
90
+ return result;
114
91
  }
115
92
 
116
- return val;
117
- };
93
+ // Read a variable-length "Length Coded Binary" integer. This is derived
94
+ // from the MySQL protocol, and re-used in the binary log format. This
95
+ // format uses the first byte to alternately store the actual value for
96
+ // integer values <= 250, or to encode the number of following bytes
97
+ // used to store the actual value, which can be 2, 3, or 8. It also
98
+ // includes support for SQL NULL as a special case.
99
+ readVariant() {
100
+ let result = null;
101
+ const firstByte = this.readUInt8();
102
+
103
+ if (firstByte < UNSIGNED_CHAR_COLUMN) {
104
+ result = firstByte;
105
+ } else if (firstByte === NULL_COLUMN) {
106
+ result = null;
107
+ } else if (firstByte === UNSIGNED_SHORT_COLUMN) {
108
+ result = this.readUInt16();
109
+ } else if (firstByte === UNSIGNED_INT24_COLUMN) {
110
+ result = this.readUInt24();
111
+ } else if (firstByte === UNSIGNED_INT64_COLUMN) {
112
+ result = this.readUInt64();
113
+ } else {
114
+ throw new Error('Invalid variable-length integer');
115
+ }
118
116
 
119
- // Read an arbitrary-length bitmap, provided its length.
120
- // Returns an array of true/false values.
121
- BufferReader.prototype.readBitArray = function(length) {
122
- const size = Math.floor((length + 7) / 8);
117
+ return result;
118
+ }
123
119
 
124
- const bytes = [];
125
- for (let i = size - 1; i >= 0; i--) {
126
- bytes.unshift(this.readUInt8());
120
+ // Read an arbitrary-length bitmap, provided its length.
121
+ // Returns an array of true/false values.
122
+ readBitArray(length) {
123
+ const size = Math.floor((length + 7) / 8);
124
+
125
+ const bytes = [];
126
+ for (let i = size - 1; i >= 0; i--) {
127
+ bytes.unshift(this.readUInt8());
128
+ }
129
+
130
+ const bitmap = [];
131
+ const bitmapStr = bytes.map(function(aByte) {
132
+ return padWith(aByte.toString(2), 8);
133
+ }).join('');
134
+
135
+ for (let k = bitmapStr.length - 1; k >= 0; k--) {
136
+ bitmap.push(bitmapStr[k] === '1');
137
+ }
138
+
139
+ return bitmap.slice(0, length);
127
140
  }
141
+ }
128
142
 
129
- const bitmap = [];
130
- const bitmapStr = bytes.map(function(aByte) {
131
- return padWith(aByte.toString(2), 8);
132
- }).join('');
143
+ class Parser {
144
+ constructor(packet) {
145
+ this.packet = null;
146
+ this._buffer = null;
147
+ this._offsetValue = 0;
148
+ this._packetEndValue = 0;
133
149
 
134
- for (let k = bitmapStr.length - 1; k >= 0; k--) {
135
- bitmap.push(bitmapStr[k] === '1');
150
+ if (packet) {
151
+ this.setPacket(packet);
152
+ }
136
153
  }
137
154
 
138
- return bitmap.slice(0, length);
139
- };
155
+ get _offset() {
156
+ return this.packet ? this.packet.offset : this._offsetValue;
157
+ }
158
+
159
+ set _offset(value) {
160
+ if (this.packet) {
161
+ this.packet.offset = value;
162
+ } else {
163
+ this._offsetValue = value;
164
+ }
165
+ }
166
+
167
+ get _packetEnd() {
168
+ return this.packet ? this.packet.end : this._packetEndValue;
169
+ }
170
+
171
+ set _packetEnd(value) {
172
+ if (this.packet) {
173
+ this.packet.end = value;
174
+ } else {
175
+ this._packetEndValue = value;
176
+ }
177
+ }
178
+
179
+ setPacket(packet) {
180
+ this.packet = packet;
181
+ this._buffer = packet.buffer;
182
+ }
183
+
184
+ append(buffer) {
185
+ this.packet = null;
186
+ this._buffer = buffer;
187
+ this._offsetValue = 0;
188
+ this._packetEndValue = buffer.length;
189
+ }
190
+
191
+ parseUnsignedNumber(bytes) {
192
+ const offset = this._offset;
193
+ this._offset += bytes;
194
+
195
+ if (bytes <= 6) {
196
+ return this._buffer.readUIntLE(offset, bytes);
197
+ }
198
+
199
+ if (bytes === 8) {
200
+ const low = this._buffer.readUInt32LE(offset);
201
+ const high = this._buffer.readUInt32LE(offset + 4);
202
+ return (high * 0x100000000) + low;
203
+ }
204
+
205
+ throw new Error('Invalid unsigned integer size: ' + bytes);
206
+ }
207
+
208
+ parseLengthCodedNumber() {
209
+ const first = this.parseUnsignedNumber(1);
210
+ if (first < 251) return first;
211
+ if (first === 251) return null;
212
+ if (first === 252) return this.parseUnsignedNumber(2);
213
+ if (first === 253) return this.parseUnsignedNumber(3);
214
+ if (first === 254) return this.parseUnsignedNumber(8);
215
+ throw new Error('Invalid length coded number');
216
+ }
217
+
218
+ parseBuffer(length) {
219
+ if (length === undefined) {
220
+ length = this._packetEnd - this._offset;
221
+ }
222
+ const start = this._offset;
223
+ this._offset += length;
224
+ return this._buffer.slice(start, start + length);
225
+ }
226
+
227
+ parseString(length) {
228
+ const buffer = this.parseBuffer(length);
229
+ return buffer.toString('utf8');
230
+ }
231
+
232
+ parsePacketTerminatedString() {
233
+ return this.parseString(this._packetEnd - this._offset);
234
+ }
235
+
236
+ parseLengthCodedString() {
237
+ const length = this.parseLengthCodedNumber();
238
+ if (length === null) return null;
239
+ return this.parseString(length);
240
+ }
241
+
242
+ reachedPacketEnd() {
243
+ return this._offset >= this._packetEnd;
244
+ }
245
+ }
140
246
 
141
- exports.BufferReader = BufferReader;
247
+ export { BufferReader, Parser };
package/lib/rows_event.js CHANGED
@@ -1,6 +1,5 @@
1
- const util = require('util');
2
- const BinlogEvent = require('./binlog_event').BinlogEvent;
3
- const Common = require('./common');
1
+ import { BinlogEvent } from './binlog_event.js';
2
+ import * as Common from './common.js';
4
3
 
5
4
  const Version2Events = [
6
5
  0x1e, // WRITE_ROWS_EVENT_V2,
@@ -39,79 +38,80 @@ const BIT_COUNT_MAP_IN_ONE_BYTE = [
39
38
  * zongji: ZongJi instance
40
39
  **/
41
40
 
42
- function RowsEvent(parser, options, zongji) {
43
- BinlogEvent.apply(this, arguments);
44
- this._zongji = zongji;
45
- this._readTableId(parser);
46
- this.flags = parser.parseUnsignedNumber(2);
47
- this.useChecksum = zongji.useChecksum;
48
-
49
- // Version 2 Events
50
- if (Version2Events.indexOf(options.eventType) !== -1) {
51
- this.extraDataLength = parser.parseUnsignedNumber(2);
52
- // skip extra data
53
- parser.parseBuffer(this.extraDataLength - 2);
54
- }
55
-
56
- // Body
57
- this.numberOfColumns = parser.parseLengthCodedNumber();
58
-
59
- this.tableMap = zongji.tableMap;
60
-
61
- const tableData = this.tableMap[this.tableId];
62
- if (tableData === undefined) {
63
- // TableMap event was filtered
64
- parser._offset = parser._packetEnd;
65
- this._filtered = true;
66
- } else {
67
- const columnsPresentBitmapSize = Math.floor((this.numberOfColumns + 7) / 8);
68
- this.columns_present_bitmap = parser.parseBuffer(columnsPresentBitmapSize);
69
- if (this._hasTwoRows) {
70
- // UpdateRows event slightly different, has new and old rows represented
71
- this.columns_present_bitmap2 = parser.parseBuffer(columnsPresentBitmapSize);
41
+ class RowsEvent extends BinlogEvent {
42
+ constructor(parser, options, zongji, hasTwoRows = false) {
43
+ super(parser, options);
44
+ this._zongji = zongji;
45
+ this._hasTwoRows = hasTwoRows;
46
+ this._readTableId(parser);
47
+ this.flags = parser.parseUnsignedNumber(2);
48
+ this.useChecksum = zongji.useChecksum;
49
+
50
+ // Version 2 Events
51
+ if (Version2Events.indexOf(options.eventType) !== -1) {
52
+ this.extraDataLength = parser.parseUnsignedNumber(2);
53
+ // skip extra data
54
+ parser.parseBuffer(this.extraDataLength - 2);
72
55
  }
73
56
 
74
- if (this.useChecksum) {
75
- // Ignore the checksum at the end of this packet
76
- parser._packetEnd -= CHECKSUM_SIZE;
77
- }
57
+ // Body
58
+ this.numberOfColumns = parser.parseLengthCodedNumber();
78
59
 
79
- this.rows = [];
80
- while (!parser.reachedPacketEnd()) {
81
- this.rows.push(this._fetchOneRow(parser));
82
- }
60
+ this.tableMap = zongji.tableMap;
83
61
 
84
- if (this.useChecksum) {
85
- // Skip past the checksum at the end of the packet
86
- parser._packetEnd += CHECKSUM_SIZE;
87
- parser._offset += CHECKSUM_SIZE;
62
+ const tableData = this.tableMap[this.tableId];
63
+ if (tableData === undefined) {
64
+ // TableMap event was filtered
65
+ parser._offset = parser._packetEnd;
66
+ this._filtered = true;
67
+ } else {
68
+ const columnsPresentBitmapSize = Math.floor((this.numberOfColumns + 7) / 8);
69
+ this.columns_present_bitmap = parser.parseBuffer(columnsPresentBitmapSize);
70
+ if (this._hasTwoRows) {
71
+ // UpdateRows event slightly different, has new and old rows represented
72
+ this.columns_present_bitmap2 = parser.parseBuffer(columnsPresentBitmapSize);
73
+ }
74
+
75
+ if (this.useChecksum) {
76
+ // Ignore the checksum at the end of this packet
77
+ parser._packetEnd -= CHECKSUM_SIZE;
78
+ }
79
+
80
+ this.rows = [];
81
+ while (!parser.reachedPacketEnd()) {
82
+ this.rows.push(this._fetchOneRow(parser));
83
+ }
84
+
85
+ if (this.useChecksum) {
86
+ // Skip past the checksum at the end of the packet
87
+ parser._packetEnd += CHECKSUM_SIZE;
88
+ parser._offset += CHECKSUM_SIZE;
89
+ }
88
90
  }
89
91
  }
90
- }
91
-
92
- util.inherits(RowsEvent, BinlogEvent);
93
92
 
94
- RowsEvent.prototype.setTableMap = function(tableMap) {
95
- this.tableMap = tableMap;
96
- };
93
+ setTableMap(tableMap) {
94
+ this.tableMap = tableMap;
95
+ }
97
96
 
98
- RowsEvent.prototype.dump = function() {
99
- BinlogEvent.prototype.dump.apply(this);
100
- console.log('Affected columns:', this.numberOfColumns);
101
- console.log('Changed rows:', this.rows.length);
102
- console.log('Values:');
103
- this.rows.forEach(function(row) {
104
- console.log('--');
105
- Object.keys(row).forEach(function(name) {
106
- console.log('Column: %s, Value: %s', name, row[name]);
97
+ dump() {
98
+ super.dump();
99
+ console.log('Affected columns:', this.numberOfColumns);
100
+ console.log('Changed rows:', this.rows.length);
101
+ console.log('Values:');
102
+ this.rows.forEach(function(row) {
103
+ console.log('--');
104
+ Object.keys(row).forEach(function(name) {
105
+ console.log('Column: %s, Value: %s', name, row[name]);
106
+ });
107
107
  });
108
- });
109
- };
108
+ }
110
109
 
111
- RowsEvent.prototype._fetchOneRow = function(parser) {
112
- const tablemap = this.tableMap[this.tableId];
113
- return readRow(parser, tablemap, this.columns_present_bitmap, this._zongji);
114
- };
110
+ _fetchOneRow(parser) {
111
+ const tablemap = this.tableMap[this.tableId];
112
+ return readRow(parser, tablemap, this.columns_present_bitmap, this._zongji);
113
+ }
114
+ }
115
115
 
116
116
  const countBits = function(buff) {
117
117
  let bits = 0;
@@ -154,47 +154,43 @@ const readRow = function(parser, tablemap, bitmap, zongji) {
154
154
  };
155
155
 
156
156
  // Subclasses
157
- function WriteRows(parser, options) { // eslint-disable-line
158
- RowsEvent.apply(this, arguments);
159
- }
160
-
161
- util.inherits(WriteRows, RowsEvent);
162
-
163
- // eslint
164
- function DeleteRows(parser, options) { // eslint-disable-line
165
- RowsEvent.apply(this, arguments);
157
+ class WriteRows extends RowsEvent {
158
+ constructor(parser, options, zongji) {
159
+ super(parser, options, zongji);
160
+ }
166
161
  }
167
162
 
168
- util.inherits(DeleteRows, RowsEvent);
169
-
170
- function UpdateRows(parser, options) { // eslint-disable-line
171
- this._hasTwoRows = true;
172
- RowsEvent.apply(this, arguments);
163
+ class DeleteRows extends RowsEvent {
164
+ constructor(parser, options, zongji) {
165
+ super(parser, options, zongji);
166
+ }
173
167
  }
174
168
 
175
- util.inherits(UpdateRows, RowsEvent);
169
+ class UpdateRows extends RowsEvent {
170
+ constructor(parser, options, zongji) {
171
+ super(parser, options, zongji, true);
172
+ }
176
173
 
177
- UpdateRows.prototype._fetchOneRow = function(parser) {
178
- const tablemap = this.tableMap[this.tableId];
179
- return {
180
- before: readRow(parser, tablemap, this.columns_present_bitmap, this._zongji),
181
- after: readRow(parser, tablemap, this.columns_present_bitmap2, this._zongji),
182
- };
183
- };
174
+ _fetchOneRow(parser) {
175
+ const tablemap = this.tableMap[this.tableId];
176
+ return {
177
+ before: readRow(parser, tablemap, this.columns_present_bitmap, this._zongji),
178
+ after: readRow(parser, tablemap, this.columns_present_bitmap2, this._zongji),
179
+ };
180
+ }
184
181
 
185
- UpdateRows.prototype.dump = function() {
186
- BinlogEvent.prototype.dump.apply(this);
187
- console.log('Affected columns:', this.numberOfColumns);
188
- console.log('Changed rows:', this.rows.length);
189
- console.log('Values:');
190
- this.rows.forEach(function(row) {
191
- console.log('--');
192
- Object.keys(row.before).forEach(function(name) {
193
- console.log('Column: %s, Value: %s => %s', name, row.before[name], row.after[name]);
182
+ dump() {
183
+ BinlogEvent.prototype.dump.call(this);
184
+ console.log('Affected columns:', this.numberOfColumns);
185
+ console.log('Changed rows:', this.rows.length);
186
+ console.log('Values:');
187
+ this.rows.forEach(function(row) {
188
+ console.log('--');
189
+ Object.keys(row.before).forEach(function(name) {
190
+ console.log('Column: %s, Value: %s => %s', name, row.before[name], row.after[name]);
191
+ });
194
192
  });
195
- });
196
- };
193
+ }
194
+ }
197
195
 
198
- exports.WriteRows = WriteRows;
199
- exports.DeleteRows = DeleteRows;
200
- exports.UpdateRows = UpdateRows;
196
+ export { WriteRows, DeleteRows, UpdateRows };