From db4cf5c2b1d0d524115e95c1ec174701ac4e7827 Mon Sep 17 00:00:00 2001 From: xiemaomao Date: Fri, 19 Jan 2024 12:45:05 +0800 Subject: [PATCH 1/2] Sync `krowinski:master` into master: - Fix JsonBinaryDecoder when there is NULL in data or if there is long. - Preserve threadId in QUERY_EVENT. --- README.md | 3 +++ src/MySQLReplication/Event/DTO/QueryDTO.php | 10 ++++++++- .../JsonBinaryDecoderService.php | 21 ++++++++++++++++--- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f499834..64a48e1 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ Based on a great work of creators:https://github.com/noplay/python-mysql-repli **Note:** Resolve these issues: - Add regular expression matching support for `DatabasesOnly` or `TablesOnly` of `Config`. + ```php + (new ConfigBuilder())->withTablesOnly(['log_\d+'])->build(); // Table `log_20240101` + ``` - Resolve [krowinski/php-mysql-replication#94](https://github.com/krowinski/php-mysql-replication/issues/94), change static config properties to non-static. - Add retry feature. ```php diff --git a/src/MySQLReplication/Event/DTO/QueryDTO.php b/src/MySQLReplication/Event/DTO/QueryDTO.php index 523dda9..6a860f9 100644 --- a/src/MySQLReplication/Event/DTO/QueryDTO.php +++ b/src/MySQLReplication/Event/DTO/QueryDTO.php @@ -12,18 +12,21 @@ class QueryDTO extends EventDTO private $query; private $database; private $type = ConstEventsNames::QUERY; + private $threadId; public function __construct( EventInfo $eventInfo, string $database, int $executionTime, - string $query + string $query, + int $threadId = 0 ) { parent::__construct($eventInfo); $this->executionTime = $executionTime; $this->query = $query; $this->database = $database; + $this->threadId = $threadId; } public function getDatabase(): string @@ -46,6 +49,11 @@ public function getType(): string return $this->type; } + public function getThreadId(): int + { + return $this->threadId; + } + public function __toString(): string { return PHP_EOL . diff --git a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php index 437b04e..01af99e 100644 --- a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php +++ b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php @@ -74,6 +74,11 @@ public static function makeJsonBinaryDecoder(string $data): self */ public function parseToString(): string { + // Sometimes, we can insert a NULL JSON even we set the JSON field as NOT NULL. + // If we meet this case, we can return a 'null' value. + if ($this->binaryDataReader->getBinaryDataLength() === 0) { + return 'null'; + } $this->parseJson($this->binaryDataReader->readUInt8()); return $this->jsonBinaryDecoderFormatter->getJsonString(); @@ -197,7 +202,7 @@ private function parseArrayOrObject(int $type, int $intSize): array $entries = []; for ($i = 0; $i !== $elementCount; ++$i) { - $entries[$i] = $this->getOffsetOrInLinedValue($bytes, $intSize); + $entries[$i] = $this->getOffsetOrInLinedValue($bytes, $intSize, $valueEntrySize); } $keys = []; @@ -237,11 +242,21 @@ private static function valueEntrySize(bool $large): int * @throws BinaryDataReaderException * @throws JsonBinaryDecoderException */ - private function getOffsetOrInLinedValue(int $bytes, int $intSize): JsonBinaryDecoderValue + private function getOffsetOrInLinedValue(int $bytes, int $intSize, int $valueEntrySize): JsonBinaryDecoderValue { $type = $this->binaryDataReader->readUInt8(); + if (self::isInLinedType($type, $intSize)) { - return $this->parseScalar($type); + $scalar = $this->parseScalar($type); + + // In binlog format, JSON arrays are fixed width elements, even though type value can be smaller. + // In order to properly process this case, we need to move cursor to the next element, which is on position 1 + $valueEntrySize (1 is length of type) + if($type === self::UINT16 || $type === self::INT16) { + $readNextBytes = $valueEntrySize - 2 - 1; + $this->binaryDataReader->read($readNextBytes); + } + + return $scalar; } $offset = $this->binaryDataReader->readUIntBySize($intSize); From 2d5e8267dc37125b5808aad4bcc3d1a00c1e4cf8 Mon Sep 17 00:00:00 2001 From: xiemaomao Date: Fri, 19 Jan 2024 13:06:24 +0800 Subject: [PATCH 2/2] Fix CS --- .../JsonBinaryDecoder/JsonBinaryDecoderService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php index 01af99e..cc4b185 100644 --- a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php +++ b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php @@ -251,7 +251,7 @@ private function getOffsetOrInLinedValue(int $bytes, int $intSize, int $valueEnt // In binlog format, JSON arrays are fixed width elements, even though type value can be smaller. // In order to properly process this case, we need to move cursor to the next element, which is on position 1 + $valueEntrySize (1 is length of type) - if($type === self::UINT16 || $type === self::INT16) { + if ($type === self::UINT16 || $type === self::INT16) { $readNextBytes = $valueEntrySize - 2 - 1; $this->binaryDataReader->read($readNextBytes); }