Skip to content

Commit

Permalink
Add MysqlEncodedValue
Browse files Browse the repository at this point in the history
Removed MysqlDataType::encodeBinary().
  • Loading branch information
trowski committed Dec 9, 2023
1 parent ccac7b1 commit 5e321f2
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 81 deletions.
11 changes: 8 additions & 3 deletions src/Internal/ConnectionProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Amp\Mysql\MysqlColumnDefinition;
use Amp\Mysql\MysqlConfig;
use Amp\Mysql\MysqlDataType;
use Amp\Mysql\MysqlEncodedValue;
use Amp\Mysql\MysqlResult;
use Amp\Parser\Parser;
use Amp\Socket\Socket;
Expand Down Expand Up @@ -465,10 +466,14 @@ public function execute(int $stmtId, string $query, array $params, array $prebou
continue;
}

[$encodedType, $value] = $paramType->encodeBinary($param);
if ($paramType === MysqlDataType::Json && \is_string($param)) {
$encodedValue = MysqlEncodedValue::fromJson($param);
} else {
$encodedValue = MysqlEncodedValue::fromValue($param);
}

$types[] = MysqlDataType::encodeInt16($encodedType->value);
$values[] = $value;
$types[] = MysqlDataType::encodeInt16($encodedValue->getType()->value);
$values[] = $encodedValue->getBytes();
}

$payload[] = $paramList;
Expand Down
66 changes: 0 additions & 66 deletions src/MysqlDataType.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,72 +39,6 @@ enum MysqlDataType: int
case String = 0xfe;
case Geometry = 0xff;

/**
* @return array{self, string}
*
* @throws SqlException If the given value cannot be encoded.
*
* @see 14.7.3 Binary Value
*/
public function encodeBinary(int|float|string|bool|\BackedEnum|\Stringable|null $param): array
{
$encodedPair = self::encodeValue($param);

if ($this === self::Json) {
[$encodedType, $encodedValue] = $encodedPair;
if ($encodedType === self::LongBlob) {
return [$this, $encodedValue];
}
}

return $encodedPair;
}

/**
* @return array{self, string}
*/
private static function encodeValue(mixed $param): array
{
switch (\get_debug_type($param)) {
case "bool":
$encoded = $param ? "\x01" : "\0";
return [self::Tiny, $encoded];

case "int":
if ($param >= -(1 << 15) && $param < (1 << 15)) {
return [self::Short, self::encodeInt16($param)];
}

if ($param >= -(1 << 31) && $param < (1 << 31)) {
return [self::Long, self::encodeInt32($param)];
}

return [self::LongLong, self::encodeInt64($param)];

case "float":
return [self::Double, \pack("e", $param)];

case "string":
return [self::LongBlob, self::encodeInt(\strlen($param)) . $param];

case "null":
return [self::Null, ""];

default:
if (\is_object($param)) {
if ($param instanceof \BackedEnum) {
return self::encodeValue($param->value);
}

if ($param instanceof \Stringable) {
return self::encodeValue((string) $param);
}
}

throw new SqlException("Unexpected type for query parameter: " . \get_debug_type($param));
}
}

/**
* @see 14.7.3 Binary Protocol Value
*
Expand Down
71 changes: 71 additions & 0 deletions src/MysqlEncodedValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php declare(strict_types=1);

namespace Amp\Mysql;

final class MysqlEncodedValue
{
public static function fromValue(mixed $param): self
{
switch (\get_debug_type($param)) {
case "string":
return new self(MysqlDataType::LongBlob, MysqlDataType::encodeInt(\strlen($param)) . $param);

case "int":
if ($param >= -(1 << 7) && $param < (1 << 7)) {
return new self(MysqlDataType::Tiny, MysqlDataType::encodeInt8($param));
}

if ($param >= -(1 << 15) && $param < (1 << 15)) {
return new self(MysqlDataType::Short, MysqlDataType::encodeInt16($param));
}

if ($param >= -(1 << 31) && $param < (1 << 31)) {
return new self(MysqlDataType::Long, MysqlDataType::encodeInt32($param));
}

return new self(MysqlDataType::LongLong, MysqlDataType::encodeInt64($param));

case "float":
return new self(MysqlDataType::Double, \pack("e", $param));

case "bool":
$encoded = $param ? "\x01" : "\0";
return new self(MysqlDataType::Tiny, $encoded);

case "null":
return new self(MysqlDataType::Null, "");

default:
if ($param instanceof \BackedEnum) {
return self::fromValue($param->value);
}

if ($param instanceof \Stringable) {
return self::fromValue((string) $param);
}

throw new \TypeError("Unexpected type for query parameter: " . \get_debug_type($param));
}
}

public static function fromJson(string $json): self
{
return new self(MysqlDataType::Json, MysqlDataType::encodeInt(\strlen($json)) . $json);
}

private function __construct(
private readonly MysqlDataType $type,
private readonly string $bytes,
) {
}

public function getType(): MysqlDataType
{
return $this->type;
}

public function getBytes(): string
{
return $this->bytes;
}
}
21 changes: 9 additions & 12 deletions test/MysqlDataTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,30 +95,27 @@ public function provideJsonData(): array
"boolean": true,
"null": null
}
JSON, false],
'array' => ['[1, 2, 3]', true],
'float' => ['3.1415926', true],
'integer' => ['42', true],
'boolean' => ['true', true],
'null' => ['null', true],
JSON],
'array' => ['[1, 2, 3]'],
'string' => ['"string"'],
'float' => ['3.1415926'],
'integer' => ['42'],
'boolean' => ['true'],
'null' => ['null'],
];
}

/**
* @dataProvider provideJsonData
*/
public function testJson(mixed $json, bool $same): void
public function testJson(mixed $json): void
{
$result = $this->connection->execute("SELECT CAST(? AS JSON) AS data", [$json]);

foreach ($result as $row) {
$expected = \json_decode($json);
$actual = \json_decode($row['data']);
if ($same) {
self::assertSame($expected, $actual);
} else {
self::assertEquals($expected, $actual);
}
self::assertEquals($expected, $actual);
}
}
}

0 comments on commit 5e321f2

Please sign in to comment.