IGNITE-10022: JS, PHP thin clients: a more meaningful exception when
ENUM type is not registered
This closes #5187
diff --git a/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php b/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
index 520063c..781a730 100644
--- a/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
+++ b/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
@@ -265,8 +265,10 @@
$ordinal = $buffer->readInteger();
$enumItem->setOrdinal($ordinal);
$type = $this->typeStorage->getType($enumItem->getTypeId());
- if (!$type->isEnum() || !$type->getEnumValues() || count($type->getEnumValues()) <= $ordinal) {
- BinaryUtils::serializationError(false, 'EnumItem can not be deserialized: type mismatch');
+ if (!$type || !$type->isEnum()) {
+ BinaryUtils::enumSerializationError(false, sprintf('enum type id "%d" is not registered', $enumItem->getTypeId()));
+ } elseif (!$type->getEnumValues() || count($type->getEnumValues()) <= $ordinal) {
+ BinaryUtils::enumSerializationError(false, 'type mismatch');
}
$enumValues = $type->getEnumValues();
$enumItem->setName($enumValues[$ordinal][0]);
@@ -396,21 +398,22 @@
private function writeEnum(MessageBuffer $buffer, EnumItem $enumValue): void
{
+ $type = $this->typeStorage->getType($enumValue->getTypeId());
+ if (!$type || !$type->isEnum()) {
+ BinaryUtils::enumSerializationError(true, sprintf('enum type id "%d" is not registered', $enumValue->getTypeId()));
+ }
$buffer->writeInteger($enumValue->getTypeId());
if ($enumValue->getOrdinal() !== null) {
$buffer->writeInteger($enumValue->getOrdinal());
return;
} elseif ($enumValue->getName() !== null || $enumValue->getValue() !== null) {
- $type = $this->typeStorage->getType($enumValue->getTypeId());
- if ($type && $type->isEnum()) {
- $enumValues = $type->getEnumValues();
- if ($enumValues) {
- for ($i = 0; $i < count($enumValues); $i++) {
- if ($enumValue->getName() === $enumValues[$i][0] ||
- $enumValue->getValue() === $enumValues[$i][1]) {
- $buffer->writeInteger($i);
- return;
- }
+ $enumValues = $type->getEnumValues();
+ if ($enumValues) {
+ for ($i = 0; $i < count($enumValues); $i++) {
+ if ($enumValue->getName() === $enumValues[$i][0] ||
+ $enumValue->getValue() === $enumValues[$i][1]) {
+ $buffer->writeInteger($i);
+ return;
}
}
}
diff --git a/src/Apache/Ignite/Internal/Binary/BinaryUtils.php b/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
index ad0bf56..e9ff2f1 100644
--- a/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
+++ b/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
@@ -203,6 +203,9 @@
$actualTypeCode === ObjectType::BINARY_OBJECT &&
$expectedTypeCode === ObjectType::COMPLEX_OBJECT) {
return;
+ } elseif ($expectedTypeCode === ObjectType::ENUM &&
+ $actualTypeCode === ObjectType::BINARY_ENUM) {
+ return;
} elseif ($actualTypeCode !== $expectedTypeCode) {
BinaryUtils::typeCastError($actualTypeCode, $expectedTypeCode);
}
@@ -419,6 +422,15 @@
throw new ClientException($msg);
}
+ public static function enumSerializationError(bool $serialize, string $message = null): void
+ {
+ $msg = $serialize ? 'Enum item can not be serialized' : 'Enum item can not be deserialized';
+ if ($message) {
+ $msg = $msg . ': ' . $message;
+ }
+ throw new ClientException($msg);
+ }
+
public static function typeCastError($fromType, $toType): void
{
throw new ClientException(sprintf('Type "%s" can not be cast to %s',
diff --git a/tests/CachePutGetTest.php b/tests/CachePutGetTest.php
index 9d15ab2..c3ff1e9 100644
--- a/tests/CachePutGetTest.php
+++ b/tests/CachePutGetTest.php
@@ -18,14 +18,20 @@
namespace Apache\Ignite\Tests;
+use \DateTime;
use Ds\Map;
use Ds\Set;
use PHPUnit\Framework\TestCase;
+use Apache\Ignite\Type\ObjectType;
use Apache\Ignite\Type\MapObjectType;
use Apache\Ignite\Type\CollectionObjectType;
use Apache\Ignite\Type\ObjectArrayType;
use Apache\Ignite\Type\ComplexObjectType;
use Apache\Ignite\Data\BinaryObject;
+use Apache\Ignite\Data\Date;
+use Apache\Ignite\Data\Timestamp;
+use Apache\Ignite\Data\EnumItem;
+use Apache\Ignite\Exception\ClientException;
class TstComplObjectWithPrimitiveFields
{
@@ -490,6 +496,119 @@
$this->putGetObjectArrays(new ObjectArrayType(new ObjectArrayType(new ComplexObjectType())), $array);
}
+ public function testPutGetDateTime(): void
+ {
+ $this->putGetDate("Y-m-d H:i:s", "2018-10-19 18:31:13", 0);
+ $this->putGetDate("Y-m-d H:i:s", "2018-10-19 18:31:13", 29726);
+ $this->putGetDate("Y-m-d H:i:s", "2018-10-19 18:31:13", 999999);
+
+ $this->putGetTimestamp("Y-m-d H:i:s", "2018-10-19 18:31:13", 0);
+ $this->putGetTimestamp("Y-m-d H:i:s", "2018-10-19 18:31:13", 29726000);
+ $this->putGetTimestamp("Y-m-d H:i:s", "2018-10-19 18:31:13", 999999999);
+
+ $this->putGetTimestampFromDateTime("Y-m-d H:i:s", "2018-10-19 18:31:13", 0);
+ $this->putGetTimestampFromDateTime("Y-m-d H:i:s", "2018-10-19 18:31:13", 29726);
+ $this->putGetTimestampFromDateTime("Y-m-d H:i:s", "2018-10-19 18:31:13", 999999);
+ }
+
+ public function testPutEnumItems(): void
+ {
+ $fakeTypeId = 12345;
+ $enumItem1 = new EnumItem($fakeTypeId);
+ $enumItem1->setOrdinal(1);
+ $this->putEnumItem($enumItem1, null);
+ $this->putEnumItem($enumItem1, ObjectType::ENUM);
+ $enumItem2 = new EnumItem($fakeTypeId);
+ $enumItem2->setName('name');
+ $this->putEnumItem($enumItem2, null);
+ $this->putEnumItem($enumItem2, ObjectType::ENUM);
+ $enumItem3 = new EnumItem($fakeTypeId);
+ $enumItem3->setOrdinal(2);
+ $this->putEnumItem($enumItem3, null);
+ $this->putEnumItem($enumItem3, ObjectType::ENUM);
+ }
+
+ private function putEnumItem($value, $valueType): void
+ {
+ $key = microtime();
+ self::$cache->
+ setKeyType(null)->
+ setValueType($valueType);
+ // Enums registration is not supported by the client, therefore put EnumItem must throw ClientException
+ try {
+ self::$cache->put($key, $value);
+ $this->fail('put EnumItem must throw ClientException');
+ } catch (ClientException $e) {
+ $this->assertContains('Enum item can not be serialized', $e->getMessage());
+ } finally {
+ self::$cache->removeAll();
+ }
+ }
+
+ private function putGetDate(string $format, string $dateString, int $micros): void
+ {
+ $key = microtime();
+ self::$cache->
+ setKeyType(null)->
+ setValueType(ObjectType::DATE);
+ try {
+ $dt = DateTime::createFromFormat("$format.u", sprintf("%s.%06d", $dateString, $micros));
+ $iDate = Date::fromDateTime($dt);
+ self::$cache->put($key, $iDate);
+ $result = self::$cache->get($key);
+
+ $this->assertEquals(sprintf("%06d", intval($micros / 1000) * 1000), $result->toDateTime()->format('u'));
+ $this->assertEquals($dateString, $result->toDateTime()->format($format));
+ } finally {
+ self::$cache->removeAll();
+ }
+ }
+
+ private function putGetTimestamp(string $format, string $dateString, int $nanos): void
+ {
+ $key = microtime();
+ self::$cache->
+ setKeyType(null)->
+ setValueType(ObjectType::TIMESTAMP);
+
+ try {
+ $millis = intval($nanos / 1000000);
+ $nanosInMillis = $nanos % 1000000;
+ self::$cache->put($key,
+ new Timestamp(
+ DateTime::createFromFormat($format, $dateString)->getTimestamp() * 1000 + $millis,
+ $nanosInMillis
+ )
+ );
+ $result = self::$cache->get($key);
+
+ $this->assertEquals($nanos % 1000000, $result->getNanos());
+ $this->assertEquals($dateString, $result->toDateTime()->format($format));
+ } finally {
+ self::$cache->removeAll();
+ }
+ }
+
+ private function putGetTimestampFromDateTime(string $format, string $dateString, $micros): void
+ {
+ $key = microtime();
+ self::$cache->
+ setKeyType(null)->
+ setValueType(ObjectType::TIMESTAMP);
+
+ try {
+ self::$cache->put($key, Timestamp::fromDateTime(
+ DateTime::createFromFormat("$format.u", sprintf("%s.%06d", $dateString, $micros))
+ ));
+ $result = self::$cache->get($key);
+
+ $this->assertEquals(intval($micros / 1000) * 1000, $result->toDateTime()->format('u'));
+ $this->assertEquals($dateString, $result->toDateTime()->format($format));
+ } finally {
+ self::$cache->removeAll();
+ }
+ }
+
private function putGetObjectArrays(?ObjectArrayType $arrayType, array $value): void
{
$key = microtime();