Дополнительные типы MessagePack
Tarantool использует предопределенные дополнительные типы MessagePack для представления некоторых специальных значений. Дополнительные типы включают MP_DECIMAL
, MP_UUID
,``MP_ERROR``, MP_DATETIME
, and MP_INTERVAL
. Эти типы требуют особого внимания со стороны разработчиков коннекторов, так как должны рассматриваться отдельно от типов MessagePack по умолчанию и корректно приводиться к типам языков программирования.
Тип MessagePack EXT MP_EXT
вместе с типом расширения MP_DECIMAL
является заголовком для значений типа DECIMAL.
MP_DECIMAL – это 1.
Спецификация MessagePack определяет два вида типов:
- типы
fixext 1/2/4/8/16
имеют фиксированную длину, поэтому длина не кодируется явно; - типы
ext 8/16/32
требуют кодирования длины данных.
MP_EXP
+ (не обязательно) length
подразумевает использование одного из этих типов.
Десятичное представление MessagePack выглядит следующим образом:
+--------+-------------------+------------+===============+
| MP_EXT | length (optional) | MP_DECIMAL | PackedDecimal |
+--------+-------------------+------------+===============+
Здесь length
– это длина поля PackedDecimal
, и оно имеет тип MP_UINT
, когда кодируется явно (т.е. когда тип – ext 8/16/32
).
PackedDecimal
имеет следующую структуру:
<--- длина в байтах -->
+-------+=============+
| scale | BCD |
+-------+=============+
Здесь scale
– это либо MP_INT
, либо MP_UINT
.
scale
= количество цифр после запятой.
BCD
- это последовательность байтов, обозначающая десятичные цифры кодируемого числа (каждый байт имеет две десятичные цифры, каждая из которых кодируется с использованием 4-битных nibbles
), поэтому byte >> 4
- первая цифра, а byte & 0x0f
- вторая цифра. Самая левая цифра в массиве - самая значимая. Самая правая цифра в массиве - наименее значимая.
Первый байт BCD
-массива содержит первую цифру числа, представленную следующим образом:
| 4 bits | 4 bits |
= 0x = the 1st digit
(Первый nibble
содержит 0, если десятичное число имеет четное число цифр). Последний байт BCD-массива содержит последнюю цифру числа и последний nibble
, представленный следующим образом:
| 4 bits | 4 bits |
= the last digit = nibble
Последний nibble
обозначает знак числа:
0x0a
,0x0c
,0x0e
,0x0f
обозначают плюс,0x0b
and0x0d
обозначают минус.
Примеры
Десятичное число -12.34
будет закодировано как 0xd6,0x01,0x02,0x01,0x23,0x4d
:
|MP_EXT (fixext 4) | MP_DECIMAL | scale | 1 | 2,3 | 4 (minus) |
| 0xd6 | 0x01 | 0x02 | 0x01 | 0x23 | 0x4d |
Десятичное число 0.000000000000000000000000000000000010 будет закодировано как 0xc7,0x03,0x01,0x24,0x01,0x0c
:
| MP_EXT (ext 8) | length | MP_DECIMAL | scale | 1 | 0 (plus) |
| 0xc7 | 0x03 | 0x01 | 0x24 | 0x01 | 0x0c |
Тип MessagePack EXT MP_EXT
вместе с типом расширения MP_UUID
является заголовком для значений типа UUID. Доступно с версии 2.4.1.
MP_UUID – это 2.
Спецификация MessagePack определяет d8
как fixext
с размером 16, а размер UUID
всегда равен 16. Таким образом, представление UUID
в MessagePack выглядит следующим образом:
+--------+------------+-----------------+
| MP_EXT | MP_UUID | UuidValue |
| = d8 | = 2 | = 16-byte value |
+--------+------------+-----------------+
The 16-byte value has 2 digits per byte. Typically, it consists of 11 fields, which are encoded as big-endian unsigned integers in the following order:
time_low
(4 bytes)time_mid
(2 bytes)time_hi_and_version
(2 bytes)clock_seq_hi_and_reserved
(1 byte)clock_seq_low
(1 byte)node[0]
, …,node[5]
(1 byte each)
Некоторые функции в модуле UUID могут выдавать значения, совместимые с типом данных UUID. Например, после
uuid = require('uuid')
box.schema.space.create('t')
box.space.t:create_index('i', {parts={1,'uuid'}})
box.space.t:insert{uuid.fromstr('f6423bdf-b49e-4913-b361-0740c9702e4b')}
box.space.t:select()
пакет ответа сервера покажет, что он содержит
d8 02 f6 42 3b df b4 9e 49 13 b3 61 07 40 c9 70 2e 4b
Начиная с версии 2.4.1, в ответах на ошибки содержится дополнительная информация, соответствующая описанию в разделе Бинарный протокол — ответы на ошибки. Это «совместимое» улучшение, потому что клиенты, которые ожидают ответы сервера старого образца, должны игнорировать компоненты ассоциативного массива, которые они не распознают. Обратите внимание, что константа IPROTO_ERROR
в ./box/iproto_constants.h
была 0x31
, а теперь IPROTO_ERROR
– 0x52
, а IPROTO_ERROR_24
– 0x31
.
MP_ERROR – это 3.
++=========================+============================+
|| | |
|| 0x31: IPROTO_ERROR_24 | 0x52: IPROTO_ERROR |
|| MP_INT: MP_STRING | MP_MAP: дополнительная информация |
|| | |
++=========================+============================+
MP_MAP
Дополнительная информация, большая часть которой также хранится в полях объекта ошибки:
MP_ERROR_TYPE
(0x00) (MP_STR) Тип, подразумевающий источник, как в error_object.base_type
, например «ClientError».
MP_ERROR_FILE
(0x01) (MP_STR) Файл с исходным кодом, в котором была перехвачена ошибка, как в error_object.trace
.
MP_ERROR_LINE
(0x02) (MP_UINT) Номер строки в файле исходных кодов, как в error_object.trace
.
MP_ERROR_MESSAGE
(0x03) (MP_STR) Текст причины, как в error_object.message
. Значение здесь будет таким же, как и значение IPROTO_ERROR_24
MP_ERROR_ERRNO
(0x04) (MP_UINT) Порядковый номер ошибки, как в error_object.errno
. Не путать с MP_ERROR_ERRCODE
.
MP_ERROR_ERRCODE
(0x05) (MP_UINT) Номер ошибки, как в файле errcode.h, как в error_object.code
, который также можно получить функцией C box_error_code(). Значение здесь будет таким же, как и в нижней части значения Response-Code-Indicator.
MP_ERROR_FIELDS
(0x06) (MP_MAPs) Дополнительные поля в зависимости от типа ошибки. Например, если MP_ERROR_TYPE
имеет значение «AccessDeniedError», то MP_ERROR_FIELDS
будет включать «object_type», «object_name», «access_type». При отсутствии дополнительных полей это поле будет пропущено в теле ответа.
Разработчики клиента и коннекторов должны убедиться, что неизвестные ключи ассоциативных массивов игнорируются, а также проверить наличие новых ключей в файле исходного кода Tarantool, в котором определено создание объекта ошибки. В версии 2.4.1 имя этого файла с исходным кодом mp_error.cc.
Например, в версии 2.4.1 или более поздней, если мы попытаемся создать дубликат пробела с помощью команды
conn:eval([[box.schema.space.create('_space');]))
,
ответ сервера будет выглядеть так:
ce 00 00 00 88 MP_UINT = HEADER + BODY SIZE
83 MP_MAP, size 3 (i.e. 3 items in header)
00 Response-Code-Indicator
ce 00 00 80 0a MP_UINT = hexadecimal 800a
01 IPROTO_SYNC
cf 00 00 00 00 00 00 00 05 MP_UINT = sync value
05 IPROTO_SCHEMA_VERSION
ce 00 00 00 4e MP_UINT = schema version value
82 MP_MAP, size 2
31 IPROTO_ERROR_24
bd 53 70 61 63 etc. MP_STR = "Space '_space' already exists"
52 IPROTO_ERROR
81 MP_MAP, size 1
00 MP_ERROR_STACK
91 MP_ARRAY, size 1
86 MP_MAP, size 6
00 MP_ERROR_TYPE
ab 43 6c 69 65 6e 74 etc. MP_STR = "ClientError"
02 MP_ERROR_LINE
cd MP_UINT = line number
01 MP_ERROR_FILE
aa 01 b6 62 75 69 6c etc. MP_STR "builtin/box/schema.lua"
03 MP_ERROR_MESSAGE
bd 53 70 61 63 65 20 etc. MP_STR = Space.'_space'.already.exists"
04 MP_ERROR_ERRNO
00 MP_UINT = error number
05 MP_ERROR_ERRCODE
0a MP_UINT = eror code ER_SPACE_EXISTS
Since version 2.10.0.
The MessagePack EXT type MP_EXT
together with the extension type
MP_DATETIME
is a header for values of the DATETIME type.
It creates a container with a payload of 8 or 16 bytes.
MP_DATETIME
type is 4.
The MessagePack specification
defines d7
to mean fixext
with size 8 or d8
to mean fixext
with size 16.
So the datetime MessagePack representation looks like this:
+---------+----------------+==========+-----------------+
| MP_EXT | MP_DATETIME | seconds | nsec; tzoffset; |
| = d7/d8 | = 4 | | tzindex; |
+---------+----------------+==========+-----------------+
MessagePack data contains:
- Seconds (8 bytes) as an unencoded 64-bit signed integer stored in the little-endian order.
- The optional fields (8 bytes), if any of them have a non-zero value.
The fields include
nsec
,tzoffset
, andtzindex
packed in the little-endian order.
For more information about the datetime type, see datetime field type details and reference for the datetime module.
Since version 2.10.0.
The MessagePack EXT type MP_EXT
together with the extension type
MP_INTERVAL
is a header for values of the INTERVAL type.
MP_INTERVAL
type is 6.
The interval is saved as a variant of a map with a predefined number of known attribute names. If some attributes are undefined, they are omitted from the generated payload.
The interval MessagePack representation looks like this:
+--------+-------------------------+-------------+----------------+
| MP_EXT | Size of packed interval | MP_INTERVAL | PackedInterval |
+--------+-------------------------+-------------+----------------+
Packed interval consists of:
- Packed number of non-zero fields.
- Packed non-null fields.
Each packed field has the following structure:
+----------+=====================+
| field ID | field value |
+----------+=====================+
The number of defined (non-null) fields can be zero. In this case, the packed interval will be encoded as integer 0.
List of the field IDs:
- 0 – year
- 1 – month
- 2 – week
- 3 – day
- 4 – hour
- 5 – minute
- 6 – second
- 7 – nanosecond
- 8 – adjust
Example
Interval value 1 years, 200 months, -77 days
is encoded in the following way:
tarantool> I = datetime.interval.new{year = 1, month = 200, day = -77}
---
...
tarantool> I
---
- +1 years, 200 months, -77 days
...
tarantool> M = msgpack.encode(I)
---
...
tarantool> M
---
- !!binary xwsGBAABAczIA9CzCAE=
...
tarantool> tohex = function(s) return (s:gsub('.', function(c) return string.format('%02X ', string.byte(c)) end)) end
---
...
tarantool> tohex(M)
---
- 'C7 0B 06 04 00 01 01 CC C8 03 D0 B3 08 01 '
...
Where:
- C7 – MP_EXT
- 0B – size of a packed interval value (11 bytes)
- 06 – MP_INTERVAL type
- 04 – number of defined fields
- 00 – field ID (year)
- 01 – packed value
1
- 01 – field ID (month)
- CCC8 – packed value
200
- 03 – field ID (day)
- D0B3 – packed value
-77
- 08 – field ID (adjust)
- 01 – packed value
1
(DT_LIMIT)
For more information about the interval type, see interval field type details and description of the datetime module.