@vlasky/zongji 0.6.1 → 0.7.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.
- package/CHANGELOG.md +24 -0
- package/README.md +30 -5
- package/example.js +5 -0
- package/index.d.ts +84 -2
- package/index.js +167 -59
- package/lib/binlog_event.js +39 -0
- package/lib/code_map.js +9 -1
- package/lib/common.js +71 -43
- package/lib/json_decode.js +61 -18
- package/lib/packet/binlog.js +7 -0
- package/lib/reader.js +4 -143
- package/lib/sequence/binlog.js +29 -7
- package/package.json +8 -5
- package/.travis.yml +0 -15
- package/docker-compose.yml +0 -33
- package/docker-test.sh +0 -11
- package/eslint.config.js +0 -35
- package/jsconfig.json +0 -15
- package/lib/packet/combinlog.js +0 -27
- package/lib/packet/index.js +0 -66
- package/scripts/start-mysql.sh +0 -28
package/lib/json_decode.js
CHANGED
|
@@ -21,11 +21,62 @@ const JSONB_LITERALS = [ null, true, false ];
|
|
|
21
21
|
// node-mysql prefixes binary string values
|
|
22
22
|
const VAR_STRING_PREFIX = 'base64:type253:';
|
|
23
23
|
|
|
24
|
+
// Decodes a JSONB binary buffer into JavaScript values (object, array,
|
|
25
|
+
// string, number, boolean, BigInt for 64-bit integers, or null). The
|
|
26
|
+
// caller converts BigInts and decides whether to serialise back to a JSON
|
|
27
|
+
// string (mysql2 `jsonStrings` option) via the helpers below.
|
|
24
28
|
export default function decodeJson(input) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
return parseBinaryBuffer(input);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
33
|
+
const MIN_SAFE_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
|
|
34
|
+
|
|
35
|
+
// For parsed-value output: BigInts become Numbers when within the safe
|
|
36
|
+
// integer range, exact strings otherwise. Mutates objects/arrays in place
|
|
37
|
+
// (they were freshly created by parseBinaryBuffer).
|
|
38
|
+
export function convertBigInts(value) {
|
|
39
|
+
if (typeof value === 'bigint') {
|
|
40
|
+
return (value >= MIN_SAFE_BIGINT && value <= MAX_SAFE_BIGINT) ?
|
|
41
|
+
Number(value) : value.toString();
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
for (let i = 0; i < value.length; i++) {
|
|
45
|
+
value[i] = convertBigInts(value[i]);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
if (value !== null && typeof value === 'object') {
|
|
50
|
+
for (const key of Object.keys(value)) {
|
|
51
|
+
value[key] = convertBigInts(value[key]);
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// For jsonStrings output: serialise to JSON text with 64-bit integers as
|
|
59
|
+
// raw numerals (JSON.stringify cannot represent BigInt), so the text
|
|
60
|
+
// matches what the server would print for the column.
|
|
61
|
+
export function jsonToText(value) {
|
|
62
|
+
if (value === null) {
|
|
63
|
+
return 'null';
|
|
64
|
+
}
|
|
65
|
+
switch (typeof value) {
|
|
66
|
+
case 'bigint':
|
|
67
|
+
case 'number':
|
|
68
|
+
case 'boolean':
|
|
69
|
+
return String(value);
|
|
70
|
+
case 'string':
|
|
71
|
+
return JSON.stringify(value);
|
|
72
|
+
default:
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return '[' + value.map(jsonToText).join(', ') + ']';
|
|
75
|
+
}
|
|
76
|
+
return '{' + Object.keys(value).map(key =>
|
|
77
|
+
JSON.stringify(key) + ': ' + jsonToText(value[key])
|
|
78
|
+
).join(', ') + '}';
|
|
79
|
+
}
|
|
29
80
|
}
|
|
30
81
|
|
|
31
82
|
/*
|
|
@@ -112,25 +163,17 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt, isLarge)
|
|
|
112
163
|
result = isLarge ? input.readUInt32LE(offset + 1) : input.readUInt32LE(valueOffset + 1);
|
|
113
164
|
break;
|
|
114
165
|
case JSONB_TYPE_INT64: {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const mask = Math.pow(2, 32) - 1;
|
|
121
|
-
high = common.sliceBits(high ^ mask, 0, 21);
|
|
122
|
-
low = low ^ mask;
|
|
123
|
-
result =
|
|
124
|
-
((high * Math.pow(2, 32)) * - 1) - common.getUInt32Value(low) - 1;
|
|
125
|
-
} else {
|
|
126
|
-
result = (high * Math.pow(2,32)) + low;
|
|
127
|
-
}
|
|
166
|
+
const low = input.readUInt32LE(valueOffset + 1);
|
|
167
|
+
const high = input.readUInt32LE(valueOffset + 5);
|
|
168
|
+
// Exact; converted to Number / string / raw JSON numeral by the
|
|
169
|
+
// consumers below
|
|
170
|
+
result = BigInt.asIntN(64, (BigInt(high) << 32n) | BigInt(low));
|
|
128
171
|
break;
|
|
129
172
|
}
|
|
130
173
|
case JSONB_TYPE_UINT64: {
|
|
131
174
|
const low = input.readUInt32LE(valueOffset + 1);
|
|
132
175
|
const high = input.readUInt32LE(valueOffset + 5);
|
|
133
|
-
result = (high
|
|
176
|
+
result = (BigInt(high) << 32n) | BigInt(low);
|
|
134
177
|
break;
|
|
135
178
|
}
|
|
136
179
|
case JSONB_TYPE_OPAQUE: {
|
package/lib/packet/binlog.js
CHANGED
|
@@ -32,6 +32,13 @@ export default function initBinlogPacketClass(zongji) {
|
|
|
32
32
|
const EventClass = getEventClass(eventType);
|
|
33
33
|
this.eventName = EventClass.name;
|
|
34
34
|
|
|
35
|
+
// Warn (once per instance) about event types that carry row changes
|
|
36
|
+
// zongji cannot decode. This must happen before event filtering so
|
|
37
|
+
// the data loss is loud even when the type is not included.
|
|
38
|
+
if (EventClass.unsupportedReason) {
|
|
39
|
+
zongji._warnUnsupportedEvent(EventClass);
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
yield;
|
|
36
43
|
|
|
37
44
|
try {
|
package/lib/reader.js
CHANGED
|
@@ -1,145 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export const UNSIGNED_SHORT_COLUMN = 252;
|
|
5
|
-
export const UNSIGNED_INT24_COLUMN = 253;
|
|
6
|
-
export const UNSIGNED_INT64_COLUMN = 254;
|
|
7
|
-
|
|
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
|
-
}
|
|
17
|
-
|
|
18
|
-
return val;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
class BufferReader {
|
|
22
|
-
constructor(buffer) {
|
|
23
|
-
this.buffer = buffer;
|
|
24
|
-
this.position = 0;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
readUInt8() {
|
|
28
|
-
const pos = this.position;
|
|
29
|
-
this.position += 1;
|
|
30
|
-
|
|
31
|
-
return this.buffer.readUInt8(pos);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
readUInt16() {
|
|
35
|
-
const pos = this.position;
|
|
36
|
-
this.position += 2;
|
|
37
|
-
|
|
38
|
-
return this.buffer.readUInt16LE(pos);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
readUInt32() {
|
|
42
|
-
const pos = this.position;
|
|
43
|
-
this.position += 4;
|
|
44
|
-
|
|
45
|
-
return this.buffer.readUInt32LE(pos);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
readUInt24() {
|
|
49
|
-
const low = this.readUInt16();
|
|
50
|
-
const high = this.readUInt8();
|
|
51
|
-
return (high << 16) + low;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
readUInt64() {
|
|
55
|
-
const pos = this.position;
|
|
56
|
-
this.position += 8;
|
|
57
|
-
|
|
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
|
-
}
|
|
62
|
-
|
|
63
|
-
readString() {
|
|
64
|
-
const strBuf = this.buffer.slice(this.position);
|
|
65
|
-
this.position = this.buffer.length;
|
|
66
|
-
|
|
67
|
-
return strBuf.toString('ascii');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
readStringInBytes(length) {
|
|
71
|
-
const strBuf = this.buffer.slice(this.position, this.position + length);
|
|
72
|
-
this.position += length;
|
|
73
|
-
|
|
74
|
-
return strBuf.toString('ascii');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
readHexInBytes(length) {
|
|
78
|
-
const buf = this.buffer.slice(this.position, this.position + length);
|
|
79
|
-
this.position += length;
|
|
80
|
-
|
|
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));
|
|
89
|
-
}
|
|
90
|
-
return result;
|
|
91
|
-
}
|
|
92
|
-
|
|
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
|
-
}
|
|
116
|
-
|
|
117
|
-
return result;
|
|
118
|
-
}
|
|
119
|
-
|
|
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);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
1
|
+
// Minimal parser over a mysql2 packet (or a bare buffer via append()),
|
|
2
|
+
// providing the subset of the node-mysql Parser interface that the binlog
|
|
3
|
+
// event classes consume.
|
|
143
4
|
class Parser {
|
|
144
5
|
constructor(packet) {
|
|
145
6
|
this.packet = null;
|
|
@@ -244,4 +105,4 @@ class Parser {
|
|
|
244
105
|
}
|
|
245
106
|
}
|
|
246
107
|
|
|
247
|
-
export {
|
|
108
|
+
export { Parser };
|
package/lib/sequence/binlog.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import
|
|
2
|
+
import initBinlogPacketClass from '../packet/binlog.js';
|
|
3
3
|
import { Parser } from '../reader.js';
|
|
4
4
|
|
|
5
5
|
const BINLOG_DUMP_COMMAND = 0x12;
|
|
@@ -137,16 +137,38 @@ export default function initBinlogClass(zongji) {
|
|
|
137
137
|
const eventName = binlogPacket.eventName
|
|
138
138
|
? binlogPacket.eventName.toLowerCase()
|
|
139
139
|
: 'unknown';
|
|
140
|
+
|
|
141
|
+
let event;
|
|
142
|
+
let error;
|
|
143
|
+
|
|
144
|
+
// Track the current transaction GTID before event filtering, so
|
|
145
|
+
// subsequent events can carry event.gtid even when 'gtid' events
|
|
146
|
+
// themselves are excluded by includeEvents
|
|
147
|
+
const isGtidEvent =
|
|
148
|
+
eventName === 'gtid' || eventName === 'anonymousgtid';
|
|
149
|
+
if (isGtidEvent) {
|
|
150
|
+
try {
|
|
151
|
+
event = binlogPacket.getEvent();
|
|
152
|
+
// Anonymous transactions have no usable GTID; do not let a
|
|
153
|
+
// previous transaction's GTID leak onto their events
|
|
154
|
+
zongji._currentGtid =
|
|
155
|
+
eventName === 'gtid' ? event.gtid : undefined;
|
|
156
|
+
} catch (err) {
|
|
157
|
+
zongji._currentGtid = undefined;
|
|
158
|
+
error = err;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
140
162
|
if (zongji._skipEvent(eventName)) {
|
|
141
163
|
return Binlog.prototype.binlogData;
|
|
142
164
|
}
|
|
143
165
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
166
|
+
if (!isGtidEvent && !error) {
|
|
167
|
+
try {
|
|
168
|
+
event = binlogPacket.getEvent();
|
|
169
|
+
} catch (err) {
|
|
170
|
+
error = err;
|
|
171
|
+
}
|
|
150
172
|
}
|
|
151
173
|
this._callback.call(this, error, event);
|
|
152
174
|
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vlasky/zongji",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "MySQL binlog-based change data capture (CDC) for Node.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./index.d.ts",
|
|
11
|
+
"default": "./index.js"
|
|
12
|
+
}
|
|
10
13
|
},
|
|
11
14
|
"scripts": {
|
|
12
15
|
"pretest": "bash scripts/start-mysql.sh",
|
|
13
16
|
"test": "tap --bail --disable-coverage --jobs=1 test/*.js",
|
|
17
|
+
"test:only": "tap --bail --disable-coverage --jobs=1 test/*.js",
|
|
14
18
|
"lint": "eslint .",
|
|
15
19
|
"checkjs": "tsc --project jsconfig.json"
|
|
16
20
|
},
|
|
@@ -42,8 +46,7 @@
|
|
|
42
46
|
"tap": "^21.5.0"
|
|
43
47
|
},
|
|
44
48
|
"dependencies": {
|
|
45
|
-
"big-integer": "1.6.52",
|
|
46
49
|
"iconv-lite": "0.7.2",
|
|
47
|
-
"mysql2": "^3.
|
|
50
|
+
"mysql2": "^3.22.5"
|
|
48
51
|
}
|
|
49
52
|
}
|
package/.travis.yml
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
language: node_js
|
|
2
|
-
sudo: required
|
|
3
|
-
dist: trusty
|
|
4
|
-
node_js:
|
|
5
|
-
- "8"
|
|
6
|
-
- "12"
|
|
7
|
-
services:
|
|
8
|
-
- docker
|
|
9
|
-
before_script:
|
|
10
|
-
- npm run lint
|
|
11
|
-
- docker run -p 3355:3306 --name mysql-55 -e MYSQL_ROOT_PASSWORD=numtel -d mysql:5.5 --server-id=1 --log-bin=/var/lib/mysql/mysql-bin.log --binlog-format=row
|
|
12
|
-
- docker run -p 3356:3306 --name mysql-56 -e MYSQL_ROOT_PASSWORD=numtel -d mysql:5.6 --server-id=1 --log-bin=/var/lib/mysql/mysql-bin.log --binlog-format=row
|
|
13
|
-
- docker run -p 3357:3306 --name mysql-57 -e MYSQL_ROOT_PASSWORD=numtel -d mysql:5.7 --server-id=1 --log-bin=/var/lib/mysql/mysql-bin.log --binlog-format=row
|
|
14
|
-
script:
|
|
15
|
-
- ./test/travis/runner.sh
|
package/docker-compose.yml
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
services:
|
|
3
|
-
mysql57:
|
|
4
|
-
image: mysql:5.7
|
|
5
|
-
command: [ "--server-id=1", "--log-bin=/var/lib/mysql/mysql-bin.log", "--binlog-format=row"]
|
|
6
|
-
networks:
|
|
7
|
-
default:
|
|
8
|
-
aliases:
|
|
9
|
-
- mysql57
|
|
10
|
-
environment:
|
|
11
|
-
MYSQL_ROOT_PASSWORD: secret
|
|
12
|
-
|
|
13
|
-
mysql80:
|
|
14
|
-
image: mysql:8.0
|
|
15
|
-
command: [ "--server-id=1", "--log-bin=/var/lib/mysql/mysql-bin.log", "--binlog-format=row"]
|
|
16
|
-
networks:
|
|
17
|
-
default:
|
|
18
|
-
aliases:
|
|
19
|
-
- mysql80
|
|
20
|
-
environment:
|
|
21
|
-
MYSQL_ROOT_PASSWORD: secret
|
|
22
|
-
|
|
23
|
-
mysql84:
|
|
24
|
-
image: mysql:8.4
|
|
25
|
-
command: [ "--server-id=1", "--log-bin=/var/lib/mysql/mysql-bin.log", "--binlog-format=row"]
|
|
26
|
-
ports:
|
|
27
|
-
- "3306:3306"
|
|
28
|
-
networks:
|
|
29
|
-
default:
|
|
30
|
-
aliases:
|
|
31
|
-
- mysql84
|
|
32
|
-
environment:
|
|
33
|
-
MYSQL_ROOT_PASSWORD: secret
|
package/docker-test.sh
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
MYSQL_HOSTS="mysql57 mysql80 mysql84"
|
|
3
|
-
|
|
4
|
-
for hostname in ${MYSQL_HOSTS}; do
|
|
5
|
-
echo $hostname + node 18
|
|
6
|
-
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:18 npm test
|
|
7
|
-
echo $hostname + node 20
|
|
8
|
-
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:20 npm test
|
|
9
|
-
echo $hostname + node 22
|
|
10
|
-
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:22 npm test
|
|
11
|
-
done
|
package/eslint.config.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import js from '@eslint/js';
|
|
2
|
-
|
|
3
|
-
export default [
|
|
4
|
-
{ ignores: ['.tap/'] },
|
|
5
|
-
js.configs.recommended,
|
|
6
|
-
{
|
|
7
|
-
languageOptions: {
|
|
8
|
-
ecmaVersion: 2022,
|
|
9
|
-
sourceType: 'module',
|
|
10
|
-
globals: {
|
|
11
|
-
Buffer: 'readonly',
|
|
12
|
-
clearImmediate: 'readonly',
|
|
13
|
-
clearInterval: 'readonly',
|
|
14
|
-
clearTimeout: 'readonly',
|
|
15
|
-
console: 'readonly',
|
|
16
|
-
process: 'readonly',
|
|
17
|
-
setImmediate: 'readonly',
|
|
18
|
-
setInterval: 'readonly',
|
|
19
|
-
setTimeout: 'readonly',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
rules: {
|
|
23
|
-
'comma-dangle': ['warn', 'only-multiline'],
|
|
24
|
-
'eol-last': ['error'],
|
|
25
|
-
'keyword-spacing': ['error', { before: true }],
|
|
26
|
-
'no-console': 'off',
|
|
27
|
-
'no-trailing-spaces': ['error', { skipBlankLines: true }],
|
|
28
|
-
'no-unused-vars': 'warn',
|
|
29
|
-
'no-var': 'warn',
|
|
30
|
-
'quotes': ['warn', 'single', 'avoid-escape'],
|
|
31
|
-
'semi': ['error', 'always'],
|
|
32
|
-
'space-before-blocks': 'error',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
];
|
package/jsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"checkJs": true,
|
|
4
|
-
"allowJs": true,
|
|
5
|
-
"noEmit": true,
|
|
6
|
-
"target": "ES2022",
|
|
7
|
-
"module": "NodeNext",
|
|
8
|
-
"moduleResolution": "NodeNext",
|
|
9
|
-
"skipDefaultLibCheck": true,
|
|
10
|
-
"maxNodeModuleJsDepth": 0,
|
|
11
|
-
"types": ["node"]
|
|
12
|
-
},
|
|
13
|
-
"include": ["index.js", "index.d.ts", "lib/**/*.js"],
|
|
14
|
-
"exclude": ["node_modules", ".tap"]
|
|
15
|
-
}
|
package/lib/packet/combinlog.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
class ComBinlog {
|
|
2
|
-
constructor({ serverId, nonBlock, filename, position }) {
|
|
3
|
-
this.command = 0x12;
|
|
4
|
-
this.position = position || 4;
|
|
5
|
-
|
|
6
|
-
// will send eof package if there is no more binlog event
|
|
7
|
-
// https://dev.mysql.com/doc/internals/en/com-binlog-dump.html#binlog-dump-non-block
|
|
8
|
-
this.flags = nonBlock ? 1 : 0;
|
|
9
|
-
|
|
10
|
-
this.serverId = serverId || 1;
|
|
11
|
-
this.filename = filename || '';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
write(writer) {
|
|
15
|
-
writer.writeUnsignedNumber(1, this.command);
|
|
16
|
-
writer.writeUnsignedNumber(4, this.position);
|
|
17
|
-
writer.writeUnsignedNumber(2, this.flags);
|
|
18
|
-
writer.writeUnsignedNumber(4, this.serverId);
|
|
19
|
-
writer.writeNullTerminatedString(this.filename);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
parse() {
|
|
23
|
-
throw new Error('should never be called here');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default ComBinlog;
|
package/lib/packet/index.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import ComBinlog from './combinlog.js';
|
|
2
|
-
import initBinlogPacketClass from './binlog.js';
|
|
3
|
-
|
|
4
|
-
// from npm package mysql
|
|
5
|
-
class EofPacket {
|
|
6
|
-
constructor(options = {}) {
|
|
7
|
-
this.fieldCount = undefined;
|
|
8
|
-
this.warningCount = options.warningCount;
|
|
9
|
-
this.serverStatus = options.serverStatus;
|
|
10
|
-
this.protocol41 = options.protocol41;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
parse(parser) {
|
|
14
|
-
this.fieldCount = parser.parseUnsignedNumber(1);
|
|
15
|
-
if (this.protocol41) {
|
|
16
|
-
this.warningCount = parser.parseUnsignedNumber(2);
|
|
17
|
-
this.serverStatus = parser.parseUnsignedNumber(2);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
write(writer) {
|
|
22
|
-
writer.writeUnsignedNumber(1, 0xfe);
|
|
23
|
-
if (this.protocol41) {
|
|
24
|
-
writer.writeUnsignedNumber(2, this.warningCount);
|
|
25
|
-
writer.writeUnsignedNumber(2, this.serverStatus);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// from npm package mysql
|
|
31
|
-
class ErrorPacket {
|
|
32
|
-
constructor(options = {}) {
|
|
33
|
-
this.fieldCount = options.fieldCount;
|
|
34
|
-
this.errno = options.errno;
|
|
35
|
-
this.sqlStateMarker = options.sqlStateMarker;
|
|
36
|
-
this.sqlState = options.sqlState;
|
|
37
|
-
this.message = options.message;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
parse(parser) {
|
|
41
|
-
this.fieldCount = parser.parseUnsignedNumber(1);
|
|
42
|
-
this.errno = parser.parseUnsignedNumber(2);
|
|
43
|
-
|
|
44
|
-
// sqlStateMarker ('#' = 0x23) indicates error packet format
|
|
45
|
-
if (parser.peak() === 0x23) {
|
|
46
|
-
this.sqlStateMarker = parser.parseString(1);
|
|
47
|
-
this.sqlState = parser.parseString(5);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
this.message = parser.parsePacketTerminatedString();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
write(writer) {
|
|
54
|
-
writer.writeUnsignedNumber(1, 0xff);
|
|
55
|
-
writer.writeUnsignedNumber(2, this.errno);
|
|
56
|
-
|
|
57
|
-
if (this.sqlStateMarker) {
|
|
58
|
-
writer.writeString(this.sqlStateMarker);
|
|
59
|
-
writer.writeString(this.sqlState);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
writer.writeString(this.message);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export { EofPacket, ErrorPacket, ComBinlog, initBinlogPacketClass };
|
package/scripts/start-mysql.sh
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Start MySQL Docker containers for testing
|
|
3
|
-
# Skips MySQL 5.7 on ARM64 (Apple Silicon) as no image is available
|
|
4
|
-
|
|
5
|
-
if [ "$(uname -m)" = "arm64" ]; then
|
|
6
|
-
SERVICES="mysql80 mysql84"
|
|
7
|
-
else
|
|
8
|
-
SERVICES="mysql57 mysql80 mysql84"
|
|
9
|
-
fi
|
|
10
|
-
|
|
11
|
-
docker-compose up -d $SERVICES
|
|
12
|
-
|
|
13
|
-
# Wait for MySQL to be ready
|
|
14
|
-
for service in $SERVICES; do
|
|
15
|
-
container="zongji-${service}-1"
|
|
16
|
-
echo -n "Waiting for ${service}..."
|
|
17
|
-
for i in $(seq 1 30); do
|
|
18
|
-
if docker exec "$container" mysqladmin ping -u root -psecret --silent 2>/dev/null; then
|
|
19
|
-
echo " ready"
|
|
20
|
-
break
|
|
21
|
-
fi
|
|
22
|
-
if [ "$i" -eq 30 ]; then
|
|
23
|
-
echo " timed out"
|
|
24
|
-
exit 1
|
|
25
|
-
fi
|
|
26
|
-
sleep 2
|
|
27
|
-
done
|
|
28
|
-
done
|