From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 8FB516F3E5; Mon, 8 Nov 2021 18:14:14 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 8FB516F3E5 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1636384454; bh=Iji/eXeM4Wr2fWqQ5i2dUiOqcki0cs7spQRwNa/J6pk=; h=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=I3tTHHQFPZ/y5QuQkqv3Pb57HXXeAclqINf61hkvivoutIA9/RylnBqfeFzwD0ynf f7EBIbsjPv5XqmSUIQfBFSasB7r8uwZYpp2WyY9h+tkQKGYYwoxrqvCx2IX/3tv1qP l6+sDV+0RscNpcsyeRD22lWsGUJXEda2aWMLAibM= Received: from smtp45.i.mail.ru (smtp45.i.mail.ru [94.100.177.105]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id BA4746F3E5 for ; Mon, 8 Nov 2021 18:14:12 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org BA4746F3E5 Received: by smtp45.i.mail.ru with esmtpa (envelope-from ) id 1mk6Lb-0001VZ-Hv; Mon, 08 Nov 2021 18:14:12 +0300 Message-ID: <68ebfc9b-5e2c-91c0-106a-8c309a31c1b3@tarantool.org> Date: Mon, 8 Nov 2021 18:14:11 +0300 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:91.0) Gecko/20100101 Thunderbird/91.2.1 To: Vladislav Shpilevoy , tarantool-patches@dev.tarantool.org, vdavydov@tarantool.org References: <0fa9f99b412387269401bed410c480b456eeb604.1636156453.git.v.shpilevoy@tarantool.org> Content-Language: en-GB In-Reply-To: <0fa9f99b412387269401bed410c480b456eeb604.1636156453.git.v.shpilevoy@tarantool.org> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-4EC0790: 10 X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD9731B3922EC063979318FB517B78237B61CEAD5F2B9D8678E00894C459B0CD1B90A90A6A3E1BA0D2C61E299D4F5694AE5AA1D1AB37C96470E93525CDB3F0CE26E X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7A179494B5629353BEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637D08ED87D5614270A8638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D8478968A8EF1809768051F80E7AB27883117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8B2EE5AD8F952D28FBA471835C12D1D977C4224003CC836476EB9C4185024447017B076A6E789B0E975F5C1EE8F4F765FCE13CD2072E3EF2E43AA81AA40904B5D9CF19DD082D7633A078D18283394535A93AA81AA40904B5D98AA50765F7900637376F9BD70FBB3917EC76A7562686271EEC990983EF5C03292E808ACE2090B5E14AD6D5ED66289B5259CC434672EE63711DD303D21008E298D5E8D9A59859A8B6B372FE9A2E580EFC725E5C173C3A84C3AA8C68A3574AB3B435872C767BF85DA2F004C90652538430E4A6367B16DE6309 X-C1DE0DAB: 0D63561A33F958A5D012F47498A0AC1819708159A8E0F64FD4897AE3B9C849FAD59269BC5F550898D99A6476B3ADF6B47008B74DF8BB9EF7333BD3B22AA88B938A852937E12ACA753753CEE10E4ED4A7410CA545F18667F91A7EA1CDA0B5A7A0 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34AEC7C2AC3C44791D60E4020DF764624C33BF28BF112F261CD7AC72078CE43758532514A167DD579C1D7E09C32AA3244CCFF07D82C2247163BA95B2983D60F81260759606DA2E136AFACE5A9C96DEB163 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2bioj9/1IdNVT2RYYIK43BMteqA== X-Mailru-Sender: 11C2EC085EDE56FA38FD4C59F7EFE40764F716D4C9291FDB258EB6A69B02EBF4CA3B0B4831EE04006BB2E709EA627F343C7DDD459B58856F0E45BC603594F5A135B915D4279FF0579437F6177E88F7363CDA0F3B3F5B9367 X-Mras: Ok Subject: Re: [Tarantool-patches] [PATCH 2/9] error: introduce error_payload X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Serge Petrenko via Tarantool-patches Reply-To: Serge Petrenko Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" 06.11.2021 02:56, Vladislav Shpilevoy пишет: > It is a dictionary-like struct which stores keys with binary data > values. The values are supposed to be arbitrary MessagePack blobs: > number, string, bool, UUID, array, map, anything. > > The payload is an array inside instead of a hash table because > number of keys will be <= 3 in all cases. And even when it will be > public, it is very unlikely it will be bigger. > > Object of error_payload in a future patch will be stored in struct > error and will allow to extend it dynamically with more members. > > This in turn is needed to extend ER_READONLY error with more > details about why it happened. > > Part of #5568 Hi! Thanks for the patch! Please, find 6 comments below. > --- > src/lib/core/CMakeLists.txt | 1 + > src/lib/core/error_payload.c | 282 +++++++++++++++++++++ > src/lib/core/error_payload.h | 166 +++++++++++++ > src/lib/uuid/mp_uuid.c | 26 ++ > src/lib/uuid/tt_uuid.h | 24 ++ > test/unit/CMakeLists.txt | 2 + > test/unit/error.c | 461 +++++++++++++++++++++++++++++++++++ > test/unit/error.result | 160 ++++++++++++ > 8 files changed, 1122 insertions(+) > create mode 100644 src/lib/core/error_payload.c > create mode 100644 src/lib/core/error_payload.h > create mode 100644 test/unit/error.c > create mode 100644 test/unit/error.result > > diff --git a/src/lib/core/CMakeLists.txt b/src/lib/core/CMakeLists.txt > index 0cc742a1c..860758515 100644 > --- a/src/lib/core/CMakeLists.txt > +++ b/src/lib/core/CMakeLists.txt > @@ -21,6 +21,7 @@ set(core_sources > fio.c > exception.cc > errinj.c > + error_payload.c > reflection.c > assoc.c > util.c > diff --git a/src/lib/core/error_payload.c b/src/lib/core/error_payload.c > new file mode 100644 > index 000000000..98ef08ada > --- /dev/null > +++ b/src/lib/core/error_payload.c > @@ -0,0 +1,282 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. > + */ > +#include "error_payload.h" > +#include "msgpuck.h" > + > +void > +error_payload_create(struct error_payload *p) > +{ > + p->count = 0; > + p->fields = NULL; > +} > + > +void > +error_payload_destroy(struct error_payload *p) > +{ > + for (int i = 0; i < p->count; ++i) { > + TRASH(p->fields[i]); > + free(p->fields[i]); > + } > + free(p->fields); > + TRASH(p); > +} > + > +const char * > +error_payload_get_str(const struct error_payload *e, const char *name) > +{ > + const struct error_field *f = error_payload_find(e, name); > + if (f == NULL) > + return NULL; > + const char *data = f->data; > + if (mp_typeof(*data) != MP_STR) > + return NULL; > + uint32_t len; > + /* > + * 0 is explicitly written after encoded string to be able to return > + * them without copying. > + */ > + data = mp_decode_str(&data, &len); > + assert(data[len] == 0); 1. Nit: `data[len] == '\0'`. Here and everywhere else. This is up to you,    I'm just more used to '\0'. > + return data; > +} > + > +void > +error_payload_set_str(struct error_payload *e, const char *name, > + const char *value) > +{ > + uint32_t len = strlen(value); > + char *data = error_payload_prepare( > + e, name, mp_sizeof_str(len), 1)->data; > + /* > + * 1 extra byte in the end is reserved to append 0 after the encoded > + * string. To be able to return it from get() without copying. > + */ 2. This comment is almost the same as the one in payload_get_str().    I propose to leave only one of them. Preferably the one in set_str(). > + *mp_encode_str(data, value, len) = 0; > +} > + > +bool > +error_payload_get_uint(const struct error_payload *e, const char *name, > + uint64_t *value) > +{ > + const struct error_field *f = error_payload_find(e, name); > + if (f == NULL) > + goto not_found; > + const char *data = f->data; > + if (mp_typeof(*data) != MP_UINT) > + goto not_found; > + *value = mp_decode_uint(&data); > + return true; > + > +not_found: > + *value = 0; > + return false; > +} > + 3. Consider this diff here.    (Similar places below do need a label, but this one doesn't). ============================ diff --git a/src/lib/core/error_payload.c b/src/lib/core/error_payload.c index 98ef08ada..cfa30919e 100644 --- a/src/lib/core/error_payload.c +++ b/src/lib/core/error_payload.c @@ -62,17 +62,14 @@ error_payload_get_uint(const struct error_payload *e, const char *name,                        uint64_t *value)  {         const struct error_field *f = error_payload_find(e, name); +       *value = 0;         if (f == NULL) -               goto not_found; +               return false;         const char *data = f->data;         if (mp_typeof(*data) != MP_UINT) -               goto not_found; +               return false;         *value = mp_decode_uint(&data);         return true; - -not_found: -       *value = 0; -       return false;  }  void ============================ > +void > +error_payload_set_uint(struct error_payload *e, const char *name, > + uint64_t value) > +{ > + char *data = error_payload_prepare( > + e, name, mp_sizeof_uint(value), 0)->data; > + mp_encode_uint(data, value); > +} > + > +bool > +error_payload_get_int(const struct error_payload *e, const char *name, > + int64_t *value) > +{ > + const struct error_field *f = error_payload_find(e, name); > + if (f == NULL) > + goto not_found; > + const char *data = f->data; > + if (mp_typeof(*data) == MP_UINT) { > + uint64_t u = mp_decode_uint(&data); > + if (u > INT64_MAX) > + goto not_found; > + *value = u; > + return true; > + } else if (mp_typeof(*data) == MP_INT) { > + *value = mp_decode_int(&data); > + return true; > + } > +not_found: > + *value = 0; > + return false; > +} > + > +void > +error_payload_set_int(struct error_payload *e, const char *name, int64_t value) > +{ > + if (value >= 0) > + return error_payload_set_uint(e, name, value); > + char *data = error_payload_prepare( > + e, name, mp_sizeof_int(value), 0)->data; > + mp_encode_int(data, value); > +} > + > +bool > +error_payload_get_double(const struct error_payload *e, const char *name, > + double *value) > +{ > + const struct error_field *f = error_payload_find(e, name); > + if (f == NULL) > + goto not_found; > + const char *data = f->data; > + if (mp_typeof(*data) == MP_DOUBLE) { > + *value = mp_decode_double(&data); > + return true; > + } else if (mp_typeof(*data) == MP_FLOAT) { > + *value = mp_decode_float(&data); > + return true; > + } 4. Why do you check for both MP_DOUBLE and MP_FLOAT here?    You only encode MP_DOUBLE.    I think we might have set_float() and get_float() one day, but looks    like they're not needed now.    get_float() would only accept MP_FLOAT then, and get_double() should    only accept MP_DOUBLE. > +not_found: > + *value = 0; > + return false; > +} > + > +void > +error_payload_set_double(struct error_payload *e, const char *name, > + double value) > +{ > + char *data = error_payload_prepare( > + e, name, mp_sizeof_double(value), 0)->data; > + mp_encode_double(data, value); > +} > + > +bool > +error_payload_get_bool(const struct error_payload *e, const char *name, > + bool *value) > +{ > + const struct error_field *f = error_payload_find(e, name); > + if (f == NULL) > + goto not_found; > + const char *data = f->data; > + if (mp_typeof(*data) != MP_BOOL) > + goto not_found; > + *value = mp_decode_bool(&data); > + return true; > + > +not_found: > + *value = false; > + return false; > +} > + > +void > +error_payload_set_bool(struct error_payload *e, const char *name, bool value) > +{ > + char *data = error_payload_prepare( > + e, name, mp_sizeof_bool(value), 0)->data; > + mp_encode_bool(data, value); > +} > + > +const char * > +error_payload_get_mp(const struct error_payload *e, const char *name, > + uint32_t *size) > +{ > + const struct error_field *f = error_payload_find(e, name); > + if (f == NULL) { > + *size = 0; > + return NULL; > + } > + *size = f->size; > + return f->data; > +} > + > +void > +error_payload_set_mp(struct error_payload *e, const char *name, > + const char *src, uint32_t size) > +{ > + char *data; > + if (mp_typeof(*src) == MP_STR) { > + data = error_payload_prepare(e, name, size, 1)->data; > + /* > + * Add the terminating zero after the buffer so as get_str() > + * would work without copying. > + */ 5. Maybe shorten this comment to "\sa error_payload_set_str()" ? > + data[size] = 0; > + } else { > + data = error_payload_prepare(e, name, size, 0)->data; > + } > + memcpy(data, src, size); > +} > + > +void > +error_payload_unset(struct error_payload *e, const char *name) > +{ > + struct error_field **fields = e->fields; > + for (int i = 0; i < e->count; ++i) { > + struct error_field *f = fields[i]; > + if (strcmp(name, f->name) != 0) > + continue; > + TRASH(f); > + free(f); > + int count = --e->count; > + if (count == 0) { > + free(fields); > + e->fields = NULL; > + return; > + } > + /* Cyclic deletion. Order does not matter in a dictionary. */ > + fields[i] = fields[count]; > + return; > + } > +} > + > +void > +error_payload_move(struct error_payload *dst, struct error_payload *src) > +{ > + for (int i = 0; i < dst->count; ++i) { > + TRASH(dst->fields[i]); > + free(dst->fields[i]); > + } > + free(dst->fields); > + dst->fields = src->fields; > + dst->count = src->count; > + src->fields = NULL; > + src->count = 0; > +} > + > +const struct error_field * > +error_payload_find(const struct error_payload *e, const char *name) > +{ > + for (int i = 0; i < e->count; ++i) { > + const struct error_field *f = e->fields[i]; > + if (strcmp(name, f->name) == 0) > + return f; > + } > + return NULL; > +} > + > +struct error_field * > +error_payload_prepare(struct error_payload *e, const char *name, > + uint32_t value_size, uint32_t extra) > +{ > + struct error_field *f; > + uint32_t name_size = strlen(name) + 1; > + uint32_t data_offset = name_size + sizeof(*f); > + uint32_t total = data_offset + value_size + extra; > + > + struct error_field **fields = e->fields; > + int count = e->count; > + int i; > + for (i = 0; i < count; ++i) { > + f = fields[i]; > + if (strcmp(name, f->name) != 0) > + continue; > + f = xrealloc(f, total); > + goto set; > + } > + e->count = ++count; > + fields = xrealloc(fields, sizeof(fields[0]) * count); > + e->fields = fields; > + f = xmalloc(total); > + memcpy(f->name, name, name_size); > +set: > + f->data = (char *)f + data_offset; > + f->size = value_size; > + fields[i] = f; > + return f; > +} > diff --git a/src/lib/core/error_payload.h b/src/lib/core/error_payload.h > new file mode 100644 > index 000000000..b6bf9ceee > --- /dev/null > +++ b/src/lib/core/error_payload.h > @@ -0,0 +1,166 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. > + */ > +#pragma once > + > +#include "trivia/util.h" > + > +#if defined(__cplusplus) > +extern "C" { > +#endif /* defined(__cplusplus) */ > + > +/** Single field of error payload. */ > +struct error_field { > + /** Data size. */ > + uint32_t size; > + /** MessagePack field value. */ > + char *data; > + /** Zero terminated field name. */ > + char name[0]; > +}; > + > +/** An array of key-value pairs. */ > +struct error_payload { > + /** Number of fields. */ > + int count; > + /** > + * Array of field pointers. Not very optimized, but the errors are > + * supposed to be rare. Also not storing them by values simplifies > + * addition of new fields and their removal. > + */ > + struct error_field **fields; > +}; > + > +/** Create error payload. */ > +void > +error_payload_create(struct error_payload *p); > + > +/** Destroy error payload. */ > +void > +error_payload_destroy(struct error_payload *p); > + > +/** > + * Get value of a payload field as a string. If it is not string or is not > + * found - return NULL. > + */ > +const char * > +error_payload_get_str(const struct error_payload *e, const char *name); > + > +/** > + * Set value of a payload field to a string. If the field existed before, it is > + * overwritten. > + */ > +void > +error_payload_set_str(struct error_payload *e, const char *name, > + const char *value); > + > +/** > + * Get value of a payload field as a uint. If it is not uint or is not found - > + * return false and the out parameter is set to 0. > + */ > +bool > +error_payload_get_uint(const struct error_payload *e, const char *name, > + uint64_t *value); > + > +/** > + * Set value of a payload field to a uint. If the field existed before, it is > + * overwritten. > + */ > +void > +error_payload_set_uint(struct error_payload *e, const char *name, > + uint64_t value); > + > +/** > + * Get value of a payload field as an int. If it is not int, or is not found, or > + * does not fit int64_t - return false and the out parameter is set to 0. > + */ > +bool > +error_payload_get_int(const struct error_payload *e, const char *name, > + int64_t *value); > + > +/** > + * Set value of a payload field to an int. If the field existed before, it is > + * overwritten. > + */ > +void > +error_payload_set_int(struct error_payload *e, const char *name, int64_t value); > + > +/** > + * Get value of a payload field as a double. If it is not double or is not > + * found - return false and the out parameter is set to 0. > + */ > +bool > +error_payload_get_double(const struct error_payload *e, const char *name, > + double *value); > + > +/** > + * Set value of a payload field to a double. If the field existed before, it is > + * overwritten. > + */ > +void > +error_payload_set_double(struct error_payload *e, const char *name, > + double value); > + > +/** > + * Get value of a payload field as a bool. If it is not bool or is not found - > + * return false and the out parameter is set to false. > + */ > +bool > +error_payload_get_bool(const struct error_payload *e, const char *name, > + bool *value); > + > +/** > + * Set value of a payload field to a bool. If the field existed before, it is > + * overwritten. > + */ > +void > +error_payload_set_bool(struct error_payload *e, const char *name, bool value); > + > +/** > + * Get MessagePack value of a payload field. If it is not found - return NULL > + * and the out parameter is set to 0. > + */ > +const char * > +error_payload_get_mp(const struct error_payload *e, const char *name, > + uint32_t *size); > + > +/** > + * Set value of a payload field to a MessagePack buffer. If the field existed > + * before, it is overwritten. > + */ > +void > +error_payload_set_mp(struct error_payload *e, const char *name, > + const char *src, uint32_t size); > + > +/** Remove the given field from the payload. */ > +void > +error_payload_unset(struct error_payload *e, const char *name); > + > +/** > + * Move all fields of one payload into another. Old fields of the destination > + * are all deleted. The source stays valid but empty. > + */ > +void > +error_payload_move(struct error_payload *dst, struct error_payload *src); > + > +/** Find a payload field by name. */ > +const struct error_field * > +error_payload_find(const struct error_payload *e, const char *name); > + > +/** > + * Prepare a payload field to get a new value. If the field didn't exist, it is > + * added. If it existed then it is reallocated if was necessary and should be > + * overwritten. Name is already filled in the field. Only need to fill the > + * value. > + * Extra parameter allows to allocate extra memory for arbitrary usage after the > + * error field and its value. > + */ > +struct error_field * > +error_payload_prepare(struct error_payload *e, const char *name, > + uint32_t value_size, uint32_t extra); > + > +#if defined(__cplusplus) > +} /* extern "C" */ > +#endif /* defined(__cplusplus) */ > diff --git a/src/lib/uuid/mp_uuid.c b/src/lib/uuid/mp_uuid.c > index b2341ae36..a60716eb5 100644 > --- a/src/lib/uuid/mp_uuid.c > +++ b/src/lib/uuid/mp_uuid.c > @@ -32,6 +32,7 @@ > #include "mp_uuid.h" > #include "msgpuck.h" > #include "mp_extension_types.h" > +#include "error_payload.h" > > inline uint32_t > mp_sizeof_uuid(void) > @@ -113,3 +114,28 @@ mp_fprint_uuid(FILE *file, const char **data, uint32_t len) > return -1; > return fprintf(file, "%s", tt_uuid_str(&uuid)); > } > + > +bool > +error_payload_get_uuid(const struct error_payload *e, const char *name, > + struct tt_uuid *uuid) > +{ > + const struct error_field *f = error_payload_find(e, name); > + if (f == NULL) > + goto not_found; > + const char *data = f->data; > + if (mp_decode_uuid(&data, uuid) == NULL) > + goto not_found; > + return true; > + > +not_found: > + *uuid = uuid_nil; > + return false; > +} > + 6. I propose to get rid of the label here and set uuid to nil unconditionally.    Would it be too expensive to do 2 struct assignments instead of one?    (uuid = uuid_nil; uuid = decoded_value). > +void > +error_payload_set_uuid(struct error_payload *e, const char *name, > + const struct tt_uuid *uuid) > +{ > + char *data = error_payload_prepare(e, name, mp_sizeof_uuid(), 0)->data; > + mp_encode_uuid(data, uuid); > +} > diff --git a/src/lib/uuid/tt_uuid.h b/src/lib/uuid/tt_uuid.h > index 70c3b98b1..866764e77 100644 > --- a/src/lib/uuid/tt_uuid.h > +++ b/src/lib/uuid/tt_uuid.h > @@ -41,6 +41,8 @@ > extern "C" { > #endif > > +struct error_payload; > + > enum { UUID_LEN = 16, UUID_STR_LEN = 36 }; > > /** > @@ -182,6 +184,28 @@ tt_uuid_str(const struct tt_uuid *uu); > int > tt_uuid_from_strl(const char *in, size_t len, struct tt_uuid *uu); > > +/** > + * Error payload couldn't be implemented in libcore because requires UUID type > + * and its methods. That would be a cyclic dep. Thus in places where need to use > + * UUID fields in error payload also need to include libuuid. > + */ > + > +/** > + * Get value of a payload field as a UUID. If it is not UUID or is not found - > + * return false and the out parameter is set to UUID with zeros. > + */ > +bool > +error_payload_get_uuid(const struct error_payload *e, const char *name, > + struct tt_uuid *uuid); > + > +/** > + * Set value of a payload field to a UUID. If the field existed before, it is > + * overwritten. > + */ > +void > +error_payload_set_uuid(struct error_payload *e, const char *name, > + const struct tt_uuid *uuid); > + > #if defined(__cplusplus) > } /* extern "C" */ > #endif > diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt > index ae8b5b9ac..0bb71ccce 100644 > --- a/test/unit/CMakeLists.txt > +++ b/test/unit/CMakeLists.txt > @@ -60,6 +60,8 @@ add_executable(xmalloc.test xmalloc.c core_test_utils.c) > target_link_libraries(xmalloc.test unit) > add_executable(datetime.test datetime.c) > target_link_libraries(datetime.test tzcode core cdt unit) > +add_executable(error.test error.c core_test_utils.c) > +target_link_libraries(error.test unit core uuid) > > add_executable(bps_tree.test bps_tree.cc) > target_link_libraries(bps_tree.test small misc) > diff --git a/test/unit/error.c b/test/unit/error.c > new file mode 100644 > index 000000000..ef17c56ec > --- /dev/null > +++ b/test/unit/error.c > @@ -0,0 +1,461 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. > + */ > +#include "error_payload.h" > +#include "msgpuck.h" > +#include "random.h" > +#include "unit.h" > +#include "uuid/mp_uuid.h" > +#include "uuid/tt_uuid.h" > + > +#include > + > +static void > +test_payload_field_str(void) > +{ > + header(); > + plan(15); > + > + struct error_payload p; > + error_payload_create(&p); > + is(p.count, 0, "no fields in the beginning"); > + is(error_payload_get_str(&p, "key"), NULL, "get_str() empty"); > + > + error_payload_set_str(&p, "key1", "value1"); > + is(p.count, 1, "++count"); > + is(strcmp(error_payload_get_str(&p, "key1"), "value1"), 0, > + "get_str() finds"); > + > + error_payload_set_str(&p, "key2", "value2"); > + is(p.count, 2, "++count"); > + is(strcmp(error_payload_get_str(&p, "key1"), "value1"), 0, > + "get_str() finds old"); > + is(strcmp(error_payload_get_str(&p, "key2"), "value2"), 0, > + "get_str() finds new"); > + is(error_payload_find(&p, "key1")->size, > + mp_sizeof_str(strlen("value1")), > + "size does not include terminating zero"); > + > + error_payload_set_str(&p, "key1", "new_value1"); > + is(p.count, 2, "field count is same"); > + is(strcmp(error_payload_get_str(&p, "key1"), "new_value1"), 0, > + "get_str() finds new value"); > + is(strcmp(error_payload_get_str(&p, "key2"), "value2"), 0, > + "get_str() finds other key old value"); > + > + error_payload_unset(&p, "key2"); > + is(p.count, 1, "--count"); > + is(strcmp(error_payload_get_str(&p, "key1"), "new_value1"), 0, > + "get_str() finds new value"); > + is(error_payload_get_str(&p, "key2"), NULL, > + "get_str() can't find deleted value"); > + > + error_payload_set_uint(&p, "key2", 1); > + is(error_payload_get_str(&p, "key2"), NULL, "wrong type"); > + > + error_payload_destroy(&p); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_payload_field_uint(void) > +{ > + header(); > + plan(17); > + > + struct error_payload p; > + error_payload_create(&p); > + uint64_t val = 1; > + ok(!error_payload_get_uint(&p, "key", &val) && val == 0, > + "get_uint() empty"); > + > + error_payload_set_uint(&p, "key1", 1); > + is(p.count, 1, "++count"); > + ok(error_payload_get_uint(&p, "key1", &val), "get_uint() finds"); > + is(val, 1, "value match"); > + > + val = 100; > + ok(!error_payload_get_uint(&p, "key2", &val), "get_uint() fails"); > + is(val, 0, "value is default"); > + > + is(error_payload_find(&p, "key1")->size, mp_sizeof_uint(1), > + "small number size"); > + > + error_payload_set_uint(&p, "key2", UINT32_MAX); > + ok(error_payload_get_uint(&p, "key2", &val), "get_uint() 32 bit"); > + is(val, UINT32_MAX, "value match"); > + is(error_payload_find(&p, "key2")->size, mp_sizeof_uint(UINT32_MAX), > + "middle number size"); > + is(p.count, 2, "field count is same"); > + > + error_payload_set_uint(&p, "key1", UINT64_MAX); > + ok(error_payload_get_uint(&p, "key1", &val) && val == UINT64_MAX, > + "value 1"); > + ok(error_payload_get_uint(&p, "key2", &val) && val == UINT32_MAX, > + "value 2"); > + > + error_payload_unset(&p, "key2"); > + is(p.count, 1, "--count"); > + ok(error_payload_get_uint(&p, "key1", &val) && val == UINT64_MAX, > + "remained value"); > + ok(!error_payload_get_uint(&p, "key2", &val) && val == 0, > + "deleted value"); > + > + error_payload_set_str(&p, "key2", "1"); > + ok(!error_payload_get_uint(&p, "key2", &val) && val == 0, "wrong type"); > + > + error_payload_destroy(&p); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_payload_field_int(void) > +{ > + header(); > + plan(20); > + > + struct error_payload p; > + error_payload_create(&p); > + int64_t val = 1; > + ok(!error_payload_get_int(&p, "key", &val) && val == 0, > + "get_int() empty"); > + > + error_payload_set_int(&p, "key1", 1); > + is(p.count, 1, "++count"); > + ok(error_payload_get_int(&p, "key1", &val), "get_int() finds"); > + is(val, 1, "value match"); > + > + val = 100; > + ok(!error_payload_get_int(&p, "key2", &val), "get_int() fails"); > + is(val, 0, "value is default"); > + > + is(error_payload_find(&p, "key1")->size, mp_sizeof_uint(1), > + "small number size"); > + > + error_payload_set_int(&p, "key2", UINT32_MAX); > + ok(error_payload_get_int(&p, "key2", &val), "get_int() 32 bit"); > + is(val, UINT32_MAX, "value match"); > + is(error_payload_find(&p, "key2")->size, mp_sizeof_uint(UINT32_MAX), > + "middle number size"); > + is(p.count, 2, "field count is same"); > + > + error_payload_set_int(&p, "key1", INT64_MAX); > + ok(error_payload_get_int(&p, "key1", &val) && val == INT64_MAX, > + "value 1"); > + ok(error_payload_get_int(&p, "key2", &val) && val == UINT32_MAX, > + "value 2"); > + > + error_payload_unset(&p, "key2"); > + is(p.count, 1, "--count"); > + ok(error_payload_get_int(&p, "key1", &val) && val == INT64_MAX, > + "remained value"); > + ok(!error_payload_get_int(&p, "key2", &val) && val == 0, > + "deleted value"); > + > + error_payload_set_str(&p, "key2", "1"); > + ok(!error_payload_get_int(&p, "key2", &val) && val == 0, "wrong type"); > + > + error_payload_set_uint(&p, "key2", (uint64_t)INT64_MAX + 1); > + ok(!error_payload_get_int(&p, "key2", &val) && val == 0, "overflow"); > + > + error_payload_set_uint(&p, "key2", 100); > + ok(error_payload_get_int(&p, "key2", &val) && val == 100, "conversion"); > + > + error_payload_set_int(&p, "key2", INT64_MIN); > + ok(error_payload_get_int(&p, "key2", &val) && val == INT64_MIN, > + "min value"); > + > + error_payload_destroy(&p); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_payload_field_double(void) > +{ > + header(); > + plan(13); > + > + struct error_payload p; > + error_payload_create(&p); > + double val = 1; > + ok(!error_payload_get_double(&p, "key", &val) && val == 0, > + "get_double() empty"); > + > + error_payload_set_double(&p, "key1", 1.5); > + is(p.count, 1, "++count"); > + ok(error_payload_get_double(&p, "key1", &val), "get_double() finds"); > + is(val, 1.5, "value match"); > + > + val = 1; > + ok(!error_payload_get_double(&p, "key2", &val), "get_double() fails"); > + is(val, 0, "value is default"); > + > + is(error_payload_find(&p, "key1")->size, mp_sizeof_double(1.5), "size"); > + > + error_payload_set_double(&p, "key2", DBL_MAX); > + ok(error_payload_get_double(&p, "key1", &val) && val == 1.5, "value 1"); > + ok(error_payload_get_double(&p, "key2", &val) && val == DBL_MAX, > + "value 2"); > + > + error_payload_unset(&p, "key2"); > + is(p.count, 1, "--count"); > + ok(error_payload_get_double(&p, "key1", &val) && val == 1.5, > + "remained value"); > + ok(!error_payload_get_double(&p, "key2", &val) && val == 0, > + "deleted value"); > + > + error_payload_set_str(&p, "key2", "1"); > + ok(!error_payload_get_double(&p, "key2", &val) && val == 0, > + "wrong type"); > + > + error_payload_destroy(&p); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_payload_field_bool(void) > +{ > + header(); > + plan(13); > + > + struct error_payload p; > + error_payload_create(&p); > + bool val = true; > + ok(!error_payload_get_bool(&p, "key", &val) && !val, > + "get_bool() empty"); > + > + error_payload_set_bool(&p, "key1", true); > + is(p.count, 1, "++count"); > + ok(error_payload_get_bool(&p, "key1", &val), "get_bool() finds"); > + ok(val, "value match"); > + > + val = true; > + ok(!error_payload_get_bool(&p, "key2", &val), "get_bool() fails"); > + ok(!val, "value is default"); > + > + error_payload_set_bool(&p, "key2", false); > + ok(error_payload_get_bool(&p, "key2", &val), "get_bool() finds"); > + ok(!val, "value match"); > + > + is(error_payload_find(&p, "key1")->size, mp_sizeof_bool(true), "size"); > + > + error_payload_unset(&p, "key2"); > + is(p.count, 1, "--count"); > + ok(error_payload_get_bool(&p, "key1", &val) && val, "remained value"); > + ok(!error_payload_get_bool(&p, "key2", &val) && !val, "deleted value"); > + > + error_payload_set_str(&p, "key2", "true"); > + ok(!error_payload_get_bool(&p, "key2", &val) && !val, "wrong type"); > + > + error_payload_destroy(&p); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_payload_field_uuid(void) > +{ > + header(); > + plan(17); > + > + struct error_payload p; > + error_payload_create(&p); > + struct tt_uuid val1; > + tt_uuid_create(&val1); > + ok(!error_payload_get_uuid(&p, "key", &val1), "get_uuid() empty"); > + ok(tt_uuid_is_nil(&val1), "default value"); > + > + tt_uuid_create(&val1); > + error_payload_set_uuid(&p, "key1", &val1); > + is(p.count, 1, "++count"); > + struct tt_uuid val2; > + ok(error_payload_get_uuid(&p, "key1", &val2), "get_uuid() finds"); > + ok(tt_uuid_is_equal(&val1, &val2), "value match"); > + > + ok(!error_payload_get_uuid(&p, "key2", &val2), "get_uuid() fails"); > + ok(tt_uuid_is_nil(&val2), "value is default"); > + > + tt_uuid_create(&val2); > + error_payload_set_uuid(&p, "key2", &val2); > + struct tt_uuid val3; > + ok(error_payload_get_uuid(&p, "key2", &val3), "get_uuid() finds"); > + ok(tt_uuid_is_equal(&val3, &val2), "value match"); > + > + is(error_payload_find(&p, "key1")->size, mp_sizeof_uuid(), "size"); > + > + error_payload_unset(&p, "key2"); > + is(p.count, 1, "--count"); > + ok(error_payload_get_uuid(&p, "key1", &val3), "remained value"); > + ok(tt_uuid_is_equal(&val1, &val3), "value match"); > + ok(!error_payload_get_uuid(&p, "key2", &val3), "deleted value"); > + ok(tt_uuid_is_nil(&val3), "value match"); > + > + error_payload_set_str(&p, "key2", "1"); > + ok(!error_payload_get_uuid(&p, "key2", &val3), "wrong type"); > + ok(tt_uuid_is_nil(&val3), "value match"); > + > + error_payload_destroy(&p); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_payload_field_mp(void) > +{ > + header(); > + plan(6); > + char buf[1024]; > + char *data; > + const char *cdata; > + uint32_t size; > + > + struct error_payload p; > + error_payload_create(&p); > + > + data = mp_encode_str(buf, "value1", 6); > + error_payload_set_mp(&p, "key1", buf, data - buf); > + is(strcmp(error_payload_get_str(&p, "key1"), "value1"), 0, "mp str"); > + > + cdata = error_payload_get_mp(&p, "key1", &size); > + is(memcmp(cdata, buf, size), 0, "mp str cmp"); > + > + data = mp_encode_uint(buf, 100); > + error_payload_set_mp(&p, "key2", buf, data - buf); > + uint64_t val; > + ok(error_payload_get_uint(&p, "key2", &val) && val == 100, "mp uint"); > + > + cdata = error_payload_get_mp(&p, "key2", &size); > + is(memcmp(cdata, buf, size), 0, "mp uint cmp"); > + > + data = mp_encode_array(buf, 1); > + data = mp_encode_uint(data, 2); > + error_payload_set_mp(&p, "key3", buf, data - buf); > + > + cdata = error_payload_get_mp(&p, "key3", &size); > + is(memcmp(cdata, buf, size), 0, "mp array"); > + > + ok(!error_payload_get_uint(&p, "key3", &val) && val == 0, > + "mp uint from array"); > + > + error_payload_destroy(&p); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_payload_unset(void) > +{ > + header(); > + plan(13); > + > + struct error_payload p; > + error_payload_create(&p); > + > + error_payload_set_uint(&p, "key1", 1); > + error_payload_set_uint(&p, "key2", 2); > + error_payload_set_uint(&p, "key3", 3); > + error_payload_set_uint(&p, "key4", 4); > + error_payload_set_uint(&p, "key5", 5); > + > + error_payload_unset(&p, "key5"); > + is(p.count, 4, "unset last, count"); > + is(error_payload_find(&p, "key5"), NULL, "unset last, key"); > + > + error_payload_unset(&p, "key1"); > + is(p.count, 3, "unset first, count"); > + is(error_payload_find(&p, "key1"), NULL, "unset first, key"); > + > + uint64_t val; > + ok(error_payload_get_uint(&p, "key2", &val) && val == 2, "check key2"); > + ok(error_payload_get_uint(&p, "key3", &val) && val == 3, "check key3"); > + ok(error_payload_get_uint(&p, "key4", &val) && val == 4, "check key4"); > + > + is(strcmp(p.fields[0]->name, "key4"), 0, "deletion is cyclic"); > + > + error_payload_unset(&p, "key2"); > + is(p.count, 2, "unset middle, count"); > + is(error_payload_find(&p, "key2"), NULL, "unset middle, key"); > + ok(error_payload_get_uint(&p, "key3", &val) && val == 3, "check key3"); > + ok(error_payload_get_uint(&p, "key4", &val) && val == 4, "check key4"); > + > + error_payload_unset(&p, "key3"); > + error_payload_unset(&p, "key4"); > + > + is(p.count, 0, "unset all"); > + > + error_payload_destroy(&p); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_payload_move(void) > +{ > + header(); > + plan(7); > + > + struct error_payload p1, p2; > + error_payload_create(&p1); > + error_payload_create(&p2); > + > + error_payload_move(&p1, &p2); > + ok(p1.count == 0 && p1.fields == NULL, "empty"); > + > + error_payload_set_str(&p1, "key", "value"); > + error_payload_move(&p1, &p2); > + ok(p1.count == 0 && p1.fields == NULL, "emptied on move"); > + > + error_payload_set_str(&p1, "key", "value"); > + error_payload_set_str(&p2, "key1", "value1"); > + error_payload_set_str(&p2, "key2", "value2"); > + error_payload_move(&p1, &p2); > + is(p1.count, 2, "got 2 fields"); > + isnt(p1.fields, NULL, "got 2 fields"); > + is(strcmp(error_payload_get_str(&p1, "key1"), "value1"), 0, "key1"); > + is(strcmp(error_payload_get_str(&p1, "key2"), "value2"), 0, "key2"); > + is(error_payload_get_str(&p1, "key"), NULL, "key"); > + > + error_payload_destroy(&p2); > + error_payload_destroy(&p1); > + > + check_plan(); > + footer(); > +} > + > +int > +main(void) > +{ > + header(); > + plan(9); > + > + random_init(); > + > + test_payload_field_str(); > + test_payload_field_uint(); > + test_payload_field_int(); > + test_payload_field_double(); > + test_payload_field_bool(); > + test_payload_field_uuid(); > + test_payload_field_mp(); > + test_payload_unset(); > + test_payload_move(); > + > + random_free(); > + > + footer(); > + return check_plan(); > +} > diff --git a/test/unit/error.result b/test/unit/error.result > new file mode 100644 > index 000000000..f0c4266e6 > --- /dev/null > +++ b/test/unit/error.result > @@ -0,0 +1,160 @@ > + *** main *** > +1..9 > + *** test_payload_field_str *** > + 1..15 > + ok 1 - no fields in the beginning > + ok 2 - get_str() empty > + ok 3 - ++count > + ok 4 - get_str() finds > + ok 5 - ++count > + ok 6 - get_str() finds old > + ok 7 - get_str() finds new > + ok 8 - size does not include terminating zero > + ok 9 - field count is same > + ok 10 - get_str() finds new value > + ok 11 - get_str() finds other key old value > + ok 12 - --count > + ok 13 - get_str() finds new value > + ok 14 - get_str() can't find deleted value > + ok 15 - wrong type > +ok 1 - subtests > + *** test_payload_field_str: done *** > + *** test_payload_field_uint *** > + 1..17 > + ok 1 - get_uint() empty > + ok 2 - ++count > + ok 3 - get_uint() finds > + ok 4 - value match > + ok 5 - get_uint() fails > + ok 6 - value is default > + ok 7 - small number size > + ok 8 - get_uint() 32 bit > + ok 9 - value match > + ok 10 - middle number size > + ok 11 - field count is same > + ok 12 - value 1 > + ok 13 - value 2 > + ok 14 - --count > + ok 15 - remained value > + ok 16 - deleted value > + ok 17 - wrong type > +ok 2 - subtests > + *** test_payload_field_uint: done *** > + *** test_payload_field_int *** > + 1..20 > + ok 1 - get_int() empty > + ok 2 - ++count > + ok 3 - get_int() finds > + ok 4 - value match > + ok 5 - get_int() fails > + ok 6 - value is default > + ok 7 - small number size > + ok 8 - get_int() 32 bit > + ok 9 - value match > + ok 10 - middle number size > + ok 11 - field count is same > + ok 12 - value 1 > + ok 13 - value 2 > + ok 14 - --count > + ok 15 - remained value > + ok 16 - deleted value > + ok 17 - wrong type > + ok 18 - overflow > + ok 19 - conversion > + ok 20 - min value > +ok 3 - subtests > + *** test_payload_field_int: done *** > + *** test_payload_field_double *** > + 1..13 > + ok 1 - get_double() empty > + ok 2 - ++count > + ok 3 - get_double() finds > + ok 4 - value match > + ok 5 - get_double() fails > + ok 6 - value is default > + ok 7 - size > + ok 8 - value 1 > + ok 9 - value 2 > + ok 10 - --count > + ok 11 - remained value > + ok 12 - deleted value > + ok 13 - wrong type > +ok 4 - subtests > + *** test_payload_field_double: done *** > + *** test_payload_field_bool *** > + 1..13 > + ok 1 - get_bool() empty > + ok 2 - ++count > + ok 3 - get_bool() finds > + ok 4 - value match > + ok 5 - get_bool() fails > + ok 6 - value is default > + ok 7 - get_bool() finds > + ok 8 - value match > + ok 9 - size > + ok 10 - --count > + ok 11 - remained value > + ok 12 - deleted value > + ok 13 - wrong type > +ok 5 - subtests > + *** test_payload_field_bool: done *** > + *** test_payload_field_uuid *** > + 1..17 > + ok 1 - get_uuid() empty > + ok 2 - default value > + ok 3 - ++count > + ok 4 - get_uuid() finds > + ok 5 - value match > + ok 6 - get_uuid() fails > + ok 7 - value is default > + ok 8 - get_uuid() finds > + ok 9 - value match > + ok 10 - size > + ok 11 - --count > + ok 12 - remained value > + ok 13 - value match > + ok 14 - deleted value > + ok 15 - value match > + ok 16 - wrong type > + ok 17 - value match > +ok 6 - subtests > + *** test_payload_field_uuid: done *** > + *** test_payload_field_mp *** > + 1..6 > + ok 1 - mp str > + ok 2 - mp str cmp > + ok 3 - mp uint > + ok 4 - mp uint cmp > + ok 5 - mp array > + ok 6 - mp uint from array > +ok 7 - subtests > + *** test_payload_field_mp: done *** > + *** test_payload_unset *** > + 1..13 > + ok 1 - unset last, count > + ok 2 - unset last, key > + ok 3 - unset first, count > + ok 4 - unset first, key > + ok 5 - check key2 > + ok 6 - check key3 > + ok 7 - check key4 > + ok 8 - deletion is cyclic > + ok 9 - unset middle, count > + ok 10 - unset middle, key > + ok 11 - check key3 > + ok 12 - check key4 > + ok 13 - unset all > +ok 8 - subtests > + *** test_payload_unset: done *** > + *** test_payload_move *** > + 1..7 > + ok 1 - empty > + ok 2 - emptied on move > + ok 3 - got 2 fields > + ok 4 - got 2 fields > + ok 5 - key1 > + ok 6 - key2 > + ok 7 - key > +ok 9 - subtests > + *** test_payload_move: done *** > + *** main: done *** -- Serge Petrenko