From 4ea690ff7073a5f39943da20bcaa7a65b0463605 Mon Sep 17 00:00:00 2001 From: Matyx Date: Fri, 12 Jan 2024 09:45:38 +0100 Subject: [PATCH] Fix JsonBinaryDecoder when there is NULL in data or if there is long array with small numbers (#96) Thank you --- .../JsonBinaryDecoderService.php | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php index dc547820..a44dee12 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(); @@ -198,7 +203,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 = []; @@ -238,11 +243,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); @@ -371,4 +386,4 @@ private function ensureOffset(?int $ensureOffset): void $this->binaryDataReader->advance($ensureOffset + 1 - $pos); } } -} \ No newline at end of file +}