From: Mergen Imeev via Tarantool-patches <tarantool-patches@dev.tarantool.org> To: v.shpilevoy@tarantool.org, tsafin@tarantool.org Cc: tarantool-patches@dev.tarantool.org Subject: [Tarantool-patches] [PATCH v5 43/52] sql: introduce mem_cast_explicit() Date: Fri, 9 Apr 2021 23:53:42 +0300 [thread overview] Message-ID: <83460913f3fdab50eb49ee0b3d84a69da6e29128.1618000037.git.imeevma@gmail.com> (raw) In-Reply-To: <cover.1618000036.git.imeevma@gmail.com> Thank you for the review! My answer and new patch below. On 30.03.2021 02:08, Vladislav Shpilevoy wrote: > Thanks for the patch! > > In function names we use subject_action_object pattern. So it > should be mem_cast_explicit(), the same for the implicit cast. Fixed. New patch: commit 83460913f3fdab50eb49ee0b3d84a69da6e29128 Author: Mergen Imeev <imeevma@gmail.com> Date: Wed Mar 17 12:27:09 2021 +0300 sql: introduce mem_cast_explicit() This patch introduces mem_cast_explicit(). This function is used to convert a MEM to a given field type according to explicit cast rules. Part of #5818 diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c index be7b47e76..45d2d5fe3 100644 --- a/src/box/sql/mem.c +++ b/src/box/sql/mem.c @@ -946,6 +946,131 @@ mem_to_str(struct Mem *mem) return -1; } +static inline int +bytes_to_uint(struct Mem *mem) +{ + bool is_neg; + int64_t i; + if (sql_atoi64(mem->z, &i, &is_neg, mem->n) != 0) + return -1; + if (is_neg) + return -1; + mem_set_uint(mem, (uint64_t)i); + return 0; +} + +static inline int +str_to_bool(struct Mem *mem) +{ + char *str = mem->z; + bool b; + const char *str_true = "TRUE"; + const char *str_false = "FALSE"; + uint32_t len_true = strlen(str_true); + uint32_t len_false = strlen(str_false); + + for (; str[0] == ' '; str++); + if (strncasecmp(str, str_true, len_true) == 0) { + b = true; + str += len_true; + } else if (strncasecmp(str, str_false, len_false) == 0) { + b = false; + str += len_false; + } else { + return -1; + } + for (; str[0] == ' '; str++); + if (str[0] != '\0') + return -1; + mem_set_bool(mem, b); + return 0; +} + +static inline int +int_to_bool(struct Mem *mem) +{ + mem->u.b = mem->u.i != 0; + mem->flags = MEM_Bool; + mem->field_type = FIELD_TYPE_BOOLEAN; + return 0; +} + +static inline int +double_to_bool(struct Mem *mem) +{ + mem->u.b = mem->u.r != 0.; + mem->flags = MEM_Bool; + mem->field_type = FIELD_TYPE_BOOLEAN; + return 0; +} + +static inline int +str_to_bin(struct Mem *mem) +{ + mem->flags = (mem->flags & (MEM_Dyn | MEM_Static | MEM_Ephem)) | + MEM_Blob; + mem->field_type = FIELD_TYPE_VARBINARY; + return 0; +} + +int +mem_cast_explicit(struct Mem *mem, enum field_type type) +{ + if ((mem->flags & MEM_Null) != 0) { + mem->field_type = type; + return 0; + } + switch (type) { + case FIELD_TYPE_UNSIGNED: + if ((mem->flags & MEM_UInt) != 0) + return 0; + if ((mem->flags & MEM_Int) != 0) + return -1; + if ((mem->flags & MEM_Blob) != 0 && + (mem->flags & MEM_Subtype) != 0) + return -1; + if ((mem->flags & (MEM_Str | MEM_Blob)) != 0) + return bytes_to_uint(mem); + if ((mem->flags & MEM_Real) != 0) + return double_to_int(mem); + if ((mem->flags & MEM_Bool) != 0) + return bool_to_int(mem); + return -1; + case FIELD_TYPE_STRING: + return mem_to_str(mem); + case FIELD_TYPE_DOUBLE: + return mem_to_double(mem); + case FIELD_TYPE_INTEGER: + return mem_to_int(mem); + case FIELD_TYPE_BOOLEAN: + if ((mem->flags & MEM_Bool) != 0) + return 0; + if ((mem->flags & (MEM_UInt | MEM_Int)) != 0) + return int_to_bool(mem); + if ((mem->flags & MEM_Str) != 0) + return str_to_bool(mem); + if ((mem->flags & MEM_Real) != 0) + return double_to_bool(mem); + return -1; + case FIELD_TYPE_VARBINARY: + if ((mem->flags & MEM_Blob) != 0) + return 0; + if ((mem->flags & MEM_Str) != 0) + return str_to_bin(mem); + return -1; + case FIELD_TYPE_NUMBER: + return mem_to_number(mem); + case FIELD_TYPE_SCALAR: + if ((mem->flags & MEM_Blob) != 0 && + (mem->flags & MEM_Subtype) != 0) + return -1; + return 0; + default: + break; + } + return -1; +} + int mem_copy(struct Mem *to, const struct Mem *from) { @@ -1647,42 +1772,6 @@ valueToText(sql_value * pVal) return pVal->z; } -/** - * According to ANSI SQL string value can be converted to boolean - * type if string consists of literal "true" or "false" and - * number of leading and trailing spaces. - * - * For instance, " tRuE " can be successfully converted to - * boolean value true. - * - * @param str String to be converted to boolean. Assumed to be - * null terminated. - * @param[out] result Resulting value of cast. - * @retval 0 If string satisfies conditions above. - * @retval -1 Otherwise. - */ -static int -str_cast_to_boolean(const char *str, bool *result) -{ - assert(str != NULL); - for (; *str == ' '; str++); - if (strncasecmp(str, SQL_TOKEN_TRUE, strlen(SQL_TOKEN_TRUE)) == 0) { - *result = true; - str += 4; - } else if (strncasecmp(str, SQL_TOKEN_FALSE, - strlen(SQL_TOKEN_FALSE)) == 0) { - *result = false; - str += 5; - } else { - return -1; - } - for (; *str != '\0'; ++str) { - if (*str != ' ') - return -1; - } - return 0; -} - /* * Convert a 64-bit IEEE double into a 64-bit signed integer. * If the double is out of range of a 64-bit signed integer then @@ -2018,113 +2107,6 @@ registerTrace(int iReg, Mem *p) { } #endif -/* - * Cast the datatype of the value in pMem according to the type - * @type. Casting is different from applying type in that a cast - * is forced. In other words, the value is converted into the desired - * type even if that results in loss of data. This routine is - * used (for example) to implement the SQL "cast()" operator. - */ -int -sqlVdbeMemCast(Mem * pMem, enum field_type type) -{ - assert(type < field_type_MAX); - if (pMem->flags & MEM_Null) - return 0; - switch (type) { - case FIELD_TYPE_SCALAR: - return 0; - case FIELD_TYPE_BOOLEAN: - if ((pMem->flags & MEM_Int) != 0) { - mem_set_bool(pMem, pMem->u.i); - return 0; - } - if ((pMem->flags & MEM_UInt) != 0) { - mem_set_bool(pMem, pMem->u.u); - return 0; - } - if ((pMem->flags & MEM_Real) != 0) { - mem_set_bool(pMem, pMem->u.r); - return 0; - } - if ((pMem->flags & MEM_Str) != 0) { - bool value; - if (str_cast_to_boolean(pMem->z, &value) != 0) - return -1; - mem_set_bool(pMem, value); - return 0; - } - if ((pMem->flags & MEM_Bool) != 0) - return 0; - return -1; - case FIELD_TYPE_INTEGER: - case FIELD_TYPE_UNSIGNED: - if ((pMem->flags & (MEM_Blob | MEM_Str)) != 0) { - bool is_neg; - int64_t val; - if (sql_atoi64(pMem->z, &val, &is_neg, pMem->n) != 0) - return -1; - if (type == FIELD_TYPE_UNSIGNED && is_neg) - return -1; - mem_set_int(pMem, val, is_neg); - return 0; - } - if ((pMem->flags & MEM_Bool) != 0) { - pMem->u.u = (uint64_t)pMem->u.b; - pMem->flags = MEM_UInt; - pMem->field_type = FIELD_TYPE_UNSIGNED; - return 0; - } - if ((pMem->flags & MEM_Real) != 0) { - double d = pMem->u.r; - if (d < 0. && d >= (double)INT64_MIN) { - pMem->u.i = (int64_t)d; - pMem->flags = MEM_Int; - pMem->field_type = FIELD_TYPE_INTEGER; - return 0; - } - if (d >= 0. && d < (double)UINT64_MAX) { - pMem->u.u = (uint64_t)d; - pMem->flags = MEM_UInt; - pMem->field_type = FIELD_TYPE_UNSIGNED; - return 0; - } - return -1; - } - if (type == FIELD_TYPE_UNSIGNED && - (pMem->flags & MEM_UInt) == 0) - return -1; - return 0; - case FIELD_TYPE_DOUBLE: - return mem_to_double(pMem); - case FIELD_TYPE_NUMBER: - return mem_to_number(pMem); - case FIELD_TYPE_VARBINARY: - if ((pMem->flags & MEM_Blob) != 0) - return 0; - if ((pMem->flags & MEM_Str) != 0) { - MemSetTypeFlag(pMem, MEM_Str); - return 0; - } - return -1; - default: - assert(type == FIELD_TYPE_STRING); - assert(MEM_Str == (MEM_Blob >> 3)); - if ((pMem->flags & MEM_Bool) != 0) { - const char *str_bool = SQL_TOKEN_BOOLEAN(pMem->u.b); - if (mem_copy_str0(pMem, str_bool) != 0) - return -1; - return 0; - } - pMem->flags |= (pMem->flags & MEM_Blob) >> 3; - sql_value_apply_type(pMem, FIELD_TYPE_STRING); - assert(pMem->flags & MEM_Str || pMem->db->mallocFailed); - pMem->flags &= - ~(MEM_Int | MEM_UInt | MEM_Real | MEM_Blob | MEM_Zero); - return 0; - } -} - /* * Make sure the given Mem is \u0000 terminated. */ @@ -2282,43 +2264,6 @@ mem_apply_type(struct Mem *record, enum field_type type) } } -/** - * Convert the numeric value contained in MEM to unsigned. - * - * @param mem The MEM that contains the numeric value. - * @retval 0 if the conversion was successful, -1 otherwise. - */ -static int -mem_convert_to_unsigned(struct Mem *mem) -{ - if ((mem->flags & MEM_UInt) != 0) - return 0; - if ((mem->flags & MEM_Int) != 0) - return -1; - if ((mem->flags & MEM_Real) == 0) - return -1; - double d = mem->u.r; - if (d < 0.0 || d >= (double)UINT64_MAX) - return -1; - mem->u.u = (uint64_t)d; - mem->flags = MEM_UInt; - mem->field_type = FIELD_TYPE_UNSIGNED; - return 0; -} - -int -mem_convert_to_numeric(struct Mem *mem, enum field_type type) -{ - assert(mem_is_num(mem) && sql_type_is_numeric(type)); - assert(type != FIELD_TYPE_NUMBER); - if (type == FIELD_TYPE_DOUBLE) - return mem_to_double(mem); - if (type == FIELD_TYPE_UNSIGNED) - return mem_convert_to_unsigned(mem); - assert(type == FIELD_TYPE_INTEGER); - return mem_to_int(mem); -} - static int sqlVdbeMemGrow(struct Mem *pMem, int n, int bPreserve) { diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h index 146d08a2a..73d2ffd6a 100644 --- a/src/box/sql/mem.h +++ b/src/box/sql/mem.h @@ -522,6 +522,10 @@ mem_to_str(struct Mem *mem); int mem_to_str0(struct Mem *mem); +/** Convert the given MEM to given type according to explicit cast rules. */ +int +mem_cast_explicit(struct Mem *mem, enum field_type type); + /** * Simple type to str convertor. It is used to simplify * error reporting. @@ -555,7 +559,6 @@ registerTrace(int iReg, Mem *p); #define memIsValid(M) !mem_is_invalid(M) #endif -int sqlVdbeMemCast(struct Mem *, enum field_type type); int sqlVdbeMemNulTerminate(struct Mem *); int sqlVdbeMemExpandBlob(struct Mem *); #define ExpandBlob(P) (mem_is_zerobin(P)? sqlVdbeMemExpandBlob(P) : 0) @@ -598,17 +601,6 @@ void sql_value_apply_type(struct Mem *val, enum field_type type); int mem_apply_type(struct Mem *record, enum field_type type); -/** - * Convert the numeric value contained in MEM to another numeric - * type. - * - * @param mem The MEM that contains the numeric value. - * @param type The type to convert to. - * @retval 0 if the conversion was successful, -1 otherwise. - */ -int -mem_convert_to_numeric(struct Mem *mem, enum field_type type); - /** Setters = Change MEM value. */ int sqlVdbeMemClearAndResize(struct Mem * pMem, int n); @@ -622,12 +614,6 @@ struct Mem *sqlValueNew(struct sql *); void releaseMemArray(Mem * p, int N); -/* - * Clear any existing type flags from a Mem and replace them with f - */ -#define MemSetTypeFlag(p, f) \ - ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) - /** Getters. */ int diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 7880af248..29634841a 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -1480,7 +1480,7 @@ case OP_Cast: { /* in1 */ pIn1 = &aMem[pOp->p1]; if (ExpandBlob(pIn1) != 0) goto abort_due_to_error; - rc = sqlVdbeMemCast(pIn1, pOp->p2); + rc = mem_cast_explicit(pIn1, pOp->p2); /* * SCALAR is not type itself, but rather an aggregation * of types. Hence, cast to this type shouldn't change @@ -2100,7 +2100,7 @@ case OP_ApplyType: { if (!mem_is_num(pIn1)) goto type_mismatch; /* Try to convert numeric-to-numeric. */ - if (mem_convert_to_numeric(pIn1, type) != 0) + if (mem_cast_explicit(pIn1, type) != 0) goto type_mismatch; } pIn1++;
next prev parent reply other threads:[~2021-04-09 20:54 UTC|newest] Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <cover.1618000036.git.imeevma@gmail.com> 2021-04-09 20:53 ` [Tarantool-patches] [PATCH v5 41/52] sql: introduce mem_to_number() Mergen Imeev via Tarantool-patches 2021-04-13 23:25 ` Mergen Imeev via Tarantool-patches 2021-04-09 20:53 ` [Tarantool-patches] [PATCH v5 42/52] sql: introduce mem_to_str() and mem_to_str0() Mergen Imeev via Tarantool-patches 2021-04-13 22:58 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-13 23:41 ` Mergen Imeev via Tarantool-patches 2021-04-09 20:53 ` Mergen Imeev via Tarantool-patches [this message] 2021-04-13 22:59 ` [Tarantool-patches] [PATCH v5 43/52] sql: introduce mem_cast_explicit() Vladislav Shpilevoy via Tarantool-patches 2021-04-14 0:01 ` Mergen Imeev via Tarantool-patches 2021-04-09 20:53 ` [Tarantool-patches] [PATCH v5 44/52] sql: introduce mem_cast_implicit() Mergen Imeev via Tarantool-patches 2021-04-13 22:59 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-14 0:05 ` Mergen Imeev via Tarantool-patches 2021-04-09 20:53 ` [Tarantool-patches] [PATCH v5 45/52] sql: introduce mem_get_int() Mergen Imeev via Tarantool-patches 2021-04-13 23:01 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-14 0:28 ` Mergen Imeev via Tarantool-patches 2021-04-14 1:17 ` Mergen Imeev via Tarantool-patches 2021-04-09 21:08 ` [Tarantool-patches] [PATCH v5 46/52] sql: introduce mem_get_uint() Mergen Imeev via Tarantool-patches 2021-04-13 23:04 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-14 0:39 ` Mergen Imeev via Tarantool-patches 2021-04-14 1:21 ` Mergen Imeev via Tarantool-patches 2021-04-09 21:08 ` [Tarantool-patches] [PATCH v5 47/52] sql: introduce mem_get_double() Mergen Imeev via Tarantool-patches 2021-04-13 23:04 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-14 1:00 ` Mergen Imeev via Tarantool-patches 2021-04-15 0:17 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-15 0:46 ` Mergen Imeev via Tarantool-patches 2021-04-09 21:08 ` [Tarantool-patches] [PATCH v5 48/52] sql: introduce mem_get_bool() Mergen Imeev via Tarantool-patches 2021-04-13 23:04 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-14 1:29 ` Mergen Imeev via Tarantool-patches 2021-04-09 21:08 ` [Tarantool-patches] [PATCH v5 49/52] sql: introduce mem_get_str0() and mem_as_str0() Mergen Imeev via Tarantool-patches 2021-04-13 23:06 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-14 1:43 ` Mergen Imeev via Tarantool-patches 2021-04-09 21:08 ` [Tarantool-patches] [PATCH v5 50/52] sql: introduce mem_get_bin() Mergen Imeev via Tarantool-patches 2021-04-09 21:08 ` [Tarantool-patches] [PATCH v5 51/52] sql: introduce mem_get_bytes_len() Mergen Imeev via Tarantool-patches 2021-04-13 23:06 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-14 1:55 ` Mergen Imeev via Tarantool-patches 2021-04-15 0:21 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-15 0:51 ` Mergen Imeev via Tarantool-patches 2021-04-09 21:08 ` [Tarantool-patches] [PATCH v5 52/52] sql: introduce mem_get_agg() Mergen Imeev via Tarantool-patches
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=83460913f3fdab50eb49ee0b3d84a69da6e29128.1618000037.git.imeevma@gmail.com \ --to=tarantool-patches@dev.tarantool.org \ --cc=imeevma@tarantool.org \ --cc=tsafin@tarantool.org \ --cc=v.shpilevoy@tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH v5 43/52] sql: introduce mem_cast_explicit()' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox