Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Negative Numbers are converted to huge numbers #108

Open
qrizly2 opened this issue Feb 20, 2024 · 2 comments
Open

Negative Numbers are converted to huge numbers #108

qrizly2 opened this issue Feb 20, 2024 · 2 comments

Comments

@qrizly2
Copy link

qrizly2 commented Feb 20, 2024

  • Operating System: Linux
  • PHP Version: 8.1.2
  • php-mysql-replication Version: v7.0.1
  • *mysql version (SELECT VERSION();): 5.7.36-log

Steps required to reproduce the problem.

  1. setup a MySQL server with Binary Log Format=ROW format
  2. update a row with a negative number (-4444)
  3. Listen with a subscriber for an update event

Expected Result.

  • Number is a negative number (-4444)

Actual Result.

  • Huge positive number (4294962852)

I already have this snippet in place to fix the values, but what should be the preferred way of doing this correctly?

    protected function fixSignedNumbers(RowsDTO $event, array $values): array
    {
        /**  @var \MySQLReplication\Event\RowEvent\ColumnDTO $column */
        foreach ($event->getTableMap()->getColumnDTOCollection() as $column) {
            if(
                isset($values[ $column->getName() ]) &&
                is_int($values[ $column->getName() ]) &&
                ! $column->isUnsigned()
            ) {
                list($type) = explode("(", $column->getFieldDTO()->getColumnType());

                switch ($type) {
                    case 'int':
                        if($values[ $column->getName() ] > 2147483647) {
                            $values[ $column->getName() ] = $values[ $column->getName() ] - 2147483647 - 2147483647 - 2;
                        }
                        break;
                    case 'mediumint':
                        if($values[ $column->getName() ] > 8388607) {
                            $values[ $column->getName() ] = $values[ $column->getName() ] - 8388607 - 8388607 - 2;
                        }
                        break;
                    case 'smallint':
                        if($values[ $column->getName() ] > 32767) {
                            $values[ $column->getName() ] = $values[ $column->getName() ] - 32767 - 32767 - 2;
                        }
                        break;
                    case 'tinyint':
                        if($values[ $column->getName() ] > 127) {
                            $values[ $column->getName() ] = $values[ $column->getName() ] - 127 - 127 - 2;
                        }
                        break;
                }
            }
        }

        return $values;
    }
@qrizly2 qrizly2 changed the title Negative Numbers are converted as huge numbers Negative Numbers are converted to huge numbers Feb 20, 2024
@qrizly2
Copy link
Author

qrizly2 commented Feb 20, 2024

I think i found the issue: but is this enough?

[MySQLReplication\Tests\Unit\BinaryDataReader\BinaryDataReaderTest]

    /**
     * @test
     */
    public function shouldNegativeReadInt32(): void
    {
        $expected = -4444;
        self::assertSame($expected, $this->getBinaryRead(self::pack32bit(-4444))->readInt32());
    }

    public static function pack32bit(int $value): string
    {
        return pack(
            'C4', ($value >> 0) & 0xFF, ($value >> 8) & 0xFF, ($value >> 16) & 0xFF, ($value >> 24) & 0xFF
        );
    }

[MySQLReplication\BinaryDataReader\BinaryDataReader]

    public function readInt32(): int
    {
        $re = unpack('i', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
        return $re >= 0x80000000 ? $re - 0x100000000 : $re;
    }

@qrizly2
Copy link
Author

qrizly2 commented Feb 20, 2024

Ok, it seems it is fine for the other int Types

[MySQLReplication\Tests\Unit\BinaryDataReader\BinaryDataReaderTest]

    /**
     * @test
     */
    public function shouldNegativeReadInt64(): void
    {
        $expected = '-4444';
        self::assertSame($expected, $this->getBinaryRead(BinaryDataReader::pack64bit(-4444))->readInt64());
    }

    /**
     * @test
     */
    public function shouldNegativeReadInt8(): void
    {
        $expected = -44;
        self::assertSame($expected, $this->getBinaryRead(self::pack8bit(-44))->readInt8());
    }

    /**
     * @test
     */
    public function shouldNegativeReadInt16(): void
    {
        $expected = -4444;
        self::assertSame($expected, $this->getBinaryRead(self::pack16bit(-4444))->readInt16());
    }

    public static function pack16bit(int $value): string
    {
        return pack(
            'C2', ($value >> 0) & 0xFF, ($value >> 8) & 0xFF
        );
    }

    public static function pack8bit(int $value): string
    {
        return pack(
            'C1', ($value >> 0) & 0xFF
        );
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant