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 7646C6EC5B; Wed, 14 Apr 2021 03:01:17 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 7646C6EC5B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1618358477; bh=t8L8/2uzfg5MaXYUq2ELx1ylm/1DdpA3owiSoEUGKcU=; h=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=bPYth1Udhxc1uezRK3diDwGv+6IsMaIybqE5ZaI/dcrGU8e5lZ9LHAGX1iTYjAo59 43AKKehMdIMQWQRu9ERF9RExuc4PRtn7+IoGFkjA48LVIqKEO/ZNxAei4iSgk+kn3C 9oLtt7pEBLcVFIneAg2OSgaxWyFtj8DXB1ycyCEc= 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 55B326EC5B for ; Wed, 14 Apr 2021 03:01:13 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 55B326EC5B Received: by smtp45.i.mail.ru with esmtpa (envelope-from ) id 1lWSy0-0007WQ-8l; Wed, 14 Apr 2021 03:01:12 +0300 Date: Wed, 14 Apr 2021 03:01:10 +0300 To: Vladislav Shpilevoy Message-ID: <20210414000110.GA115746@tarantool.org> References: <83460913f3fdab50eb49ee0b3d84a69da6e29128.1618000037.git.imeevma@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD92FFCB8E6708E74806859AC5FE18436AEED970E897805ADA4182A05F53808504012261B3C7C66AE14F8F4E3476B4422D8AC0FB674836DEDC3F3E9AFA13A624D44 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE73AA63C5F29446501EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637C05E8F374EA5A79AEA1F7E6F0F101C67CDEEF6D7F21E0D1D9295C2E9FA3191EE1B59CA4C82EFA658F220B0D9E685C766D1C9F3A671067874F6B57BC7E64490618DEB871D839B73339E8FC8737B5C22498424CA1AAF98A6958941B15DA834481FCF19DD082D7633A0EF3E4896CB9E6436389733CBF5DBD5E9D5E8D9A59859A8B68424CA1AAF98A6958941B15DA834481F9449624AB7ADAF37BA3038C0950A5D3613377AFFFEAFD26923F8577A6DFFEA7C910E47378E270D9B7B076A6E789B0E97A8DF7F3B2552694A1E7802607F20496D49FD398EE364050F0AC5B80A05675ACDB1CA5D0BF4193578B3661434B16C20AC78D18283394535A9E827F84554CEF50127C277FBC8AE2E8BA83251EDC214901ED5E8D9A59859A8B62CFFCC7B69C47339089D37D7C0E48F6C5571747095F342E88FB05168BE4CE3AF X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A24209795067102C07E8F7B195E1C97831277C15DA3FEF8C2E8FF110C78C8E6CED X-C1DE0DAB: 0D63561A33F958A530D4A74FBED11A36A8CA7285CC33E2C27F8550B257F25E7BD59269BC5F550898D99A6476B3ADF6B47008B74DF8BB9EF7333BD3B22AA88B938A852937E12ACA7502E6951B79FF9A3F410CA545F18667F91A7EA1CDA0B5A7A0 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D3433E9BC74ABA5769F625F688720A65E5609E3C66E8728B09D944303235285AE7DE4FE2C7ECC670D761D7E09C32AA3244C9688B4B035C4FA6323605BCE3F39D4CD250262A5EE9971B0FACE5A9C96DEB163 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojnA7/qPBUIXHidB0Wbvcnsg== X-Mailru-Sender: 5C3750E245F362008BC1685FEC6306EDD2BAFA645AFF0F7BF8F4E3476B4422D8FE5F7FEA15315F165105BD0848736F9966FEC6BF5C9C28D97E07721503EA2E00ED97202A5A4E92BF7402F9BA4338D657ED14614B50AE0675 X-Mras: Ok Subject: Re: [Tarantool-patches] [PATCH v5 43/52] sql: introduce mem_cast_explicit() 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: Mergen Imeev via Tarantool-patches Reply-To: Mergen Imeev Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" Thank you for the review! My answers, diff and new patch below. On Wed, Apr 14, 2021 at 12:59:31AM +0200, Vladislav Shpilevoy wrote: > Thanks for the patch! > > See 2 comments below. > > > 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; > > +} > > 1. You have tons of _to_ converters. I propose you to group them. > For example, str_to_* all together, double_to_* all together, and so on. > It would simplify reading and search. > Fixed. Now the are in some order. I tried to keep sorting by second type too, but not sure that I succeed. > > + > > +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); > > 2. tarantool> box.execute('SELECT CAST(-1.1 AS UNSIGNED);') > --- > - metadata: > - name: COLUMN_1 > type: unsigned > rows: > - [-1] > ... > > That looks quite broken. Is this a known issue? From the > code I see the issue existed before your patch (but I was > too lazy to try it). > I knew about it, though I did not see an issue on GH. I will fill one later, if I will find nothing. > > @@ -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; > > - } > > -} > > It is fascinating how a good code structure allows to get rid of > all of that old garbage mess almost naturally, and reveals some > issues. Absolutly agree! Though I feel that there were found more like a ton of issues. Diff: diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c index dd979af05..4b619b032 100644 --- a/src/box/sql/mem.c +++ b/src/box/sql/mem.c @@ -517,6 +517,15 @@ int_to_str0(struct Mem *mem) return mem_copy_str0(mem, str); } +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 str_to_str0(struct Mem *mem) { @@ -529,6 +538,42 @@ str_to_str0(struct Mem *mem) 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; +} + +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 bin_to_str(struct Mem *mem) { @@ -564,6 +609,19 @@ bytes_to_int(struct Mem *mem) return 0; } +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 bytes_to_double(struct Mem *mem) { @@ -624,6 +682,15 @@ double_to_str0(struct Mem *mem) 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 bool_to_int(struct Mem *mem) { @@ -757,73 +824,6 @@ 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) { New patch: commit e43b89395710a5beb7e38ef2615856412ddec390 Author: Mergen Imeev 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 830fc3840..4b619b032 100644 --- a/src/box/sql/mem.c +++ b/src/box/sql/mem.c @@ -517,6 +517,15 @@ int_to_str0(struct Mem *mem) return mem_copy_str0(mem, str); } +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 str_to_str0(struct Mem *mem) { @@ -529,6 +538,42 @@ str_to_str0(struct Mem *mem) 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; +} + +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 bin_to_str(struct Mem *mem) { @@ -564,6 +609,19 @@ bytes_to_int(struct Mem *mem) return 0; } +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 bytes_to_double(struct Mem *mem) { @@ -624,6 +682,15 @@ double_to_str0(struct Mem *mem) 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 bool_to_int(struct Mem *mem) { @@ -757,6 +824,64 @@ mem_to_str(struct Mem *mem) return -1; } +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) { @@ -1423,42 +1548,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 @@ -1794,113 +1883,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. */ @@ -2058,43 +2040,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 454553d92..7cced5537 100644 --- a/src/box/sql/mem.h +++ b/src/box/sql/mem.h @@ -732,6 +732,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. @@ -765,7 +769,6 @@ registerTrace(int iReg, Mem *p); #define memIsValid(M) ((M)->flags & MEM_Undefined)==0 #endif -int sqlVdbeMemCast(struct Mem *, enum field_type type); int sqlVdbeMemNulTerminate(struct Mem *); int sqlVdbeMemExpandBlob(struct Mem *); #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlVdbeMemExpandBlob(P):0) @@ -808,17 +811,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); @@ -832,12 +824,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 de6455c50..049965bd0 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -1481,7 +1481,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 @@ -2145,7 +2145,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++;