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 580047034A; Tue, 23 Mar 2021 12:57:19 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 580047034A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1616493439; bh=+DwNgioLSRW83ueH4bvNODcBBN7cTaxruSM22kYRRYo=; h=To:Cc:Date:In-Reply-To:References:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=GeVl5aL9JH9sRqFdlYtd1/i3azx8Tm2MUDb2anX+/JCXqc2hL1MVx9V3BgIUlme74 XZQRI4FRNsvoSweL8Y6nV1Xn7l2Nkzla1agMjt08Kl0uVMbMij4qTUeDgHIczOPC58 jYP6JdaCcoaRs+kpOknczHSoNUoEFSPpiUFUt7uo= Received: from smtp63.i.mail.ru (smtp63.i.mail.ru [217.69.128.43]) (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 665DF68F62 for ; Tue, 23 Mar 2021 12:36:38 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 665DF68F62 Received: by smtp63.i.mail.ru with esmtpa (envelope-from ) id 1lOdSn-0002iR-Hz; Tue, 23 Mar 2021 12:36:38 +0300 To: v.shpilevoy@tarantool.org, tsafin@tarantool.org Cc: tarantool-patches@dev.tarantool.org Date: Tue, 23 Mar 2021 12:36:37 +0300 Message-Id: <55c550b284286e78dab2bfe40f5e7e736de285ed.1616491731.git.imeevma@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD95D6E7CC48CB1F5F10D3016C09B407F8B88411E9FEB481E8E182A05F538085040FE411A40F270A8BCCCDFFBD4F262E244D23C435FDE902436DC1E0274E9F68336 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7235646FAB97B4BEDEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F790063721F819F5952827038638F802B75D45FF914D58D5BE9E6BC131B5C99E7648C95C686AF563A045C75EF2038726A3FF23821A7F03DB7FD104DAA471835C12D1D9774AD6D5ED66289B5259CC434672EE6371117882F4460429724CE54428C33FAD30A8DF7F3B2552694AC26CFBAC0749D213D2E47CDBA5A9658378DA827A17800CE77A825AB47F0FC8649FA2833FD35BB23DF004C90652538430302FCEF25BFAB3454AD6D5ED66289B5278DA827A17800CE7AF0B556A5A327A457B076A6E789B0E97A8DF7F3B2552694A1E7802607F20496D49FD398EE364050FC8105B04EFE07628985B8ACC81218E19B3661434B16C20AC78D18283394535A9E827F84554CEF5019E625A9149C048EE9ECD01F8117BC8BEE2021AF6380DFAD1CF19DD082D7633A0C77107234E2CFBA567F23339F89546C55F5C1EE8F4F765FC953A8A48A05D51F175ECD9A6C639B01BBD4B6F7A4D31EC0BC0CAF46E325F83A522CA9DD8327EE4930A3850AC1BE2E73542F54486E6D6388DC4224003CC836476C0CAF46E325F83A50BF2EBBBDD9D6B0FECB2555BB02FD5A93B503F486389A921A5CC5B56E945C8DA X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A24A6D60772A99906F8E1CD14B953EB46D5C7E03876BECF115355D89D7DBCDD132 X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975C686AF563A045C75EF2038726A3FF23821A7F03DB7FD104DA9C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EF0417BEADF48D1460699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D3494FB0335DF05DC3A30BC20AF7D80B1B6C2126B98549688E860D6CD85B1446046680FCA008B8D84071D7E09C32AA3244C81928841C8B8B35DFABE8B0B7CA8D3CF8580396430872480FACE5A9C96DEB163 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojWJZv20R+6Uhp4pgEH1Cp3w== X-Mailru-Sender: 5C3750E245F362008BC1685FEC6306ED75C864E6C7AE448DCCDFFBD4F262E2441442BDFAA49391845105BD0848736F9966FEC6BF5C9C28D97E07721503EA2E00ED97202A5A4E92BF7402F9BA4338D657ED14614B50AE0675 X-Mras: Ok Subject: [Tarantool-patches] [PATCH v4 45/53] sql: introduce mem_explicit_cast() 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: imeevma@tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" This patch introduces mem_explicit_cast(). This function is used to convert a MEM to a given field type according to explicit cast rules. Part of #5818 --- src/box/sql/mem.c | 302 +++++++++++++++++++-------------------------- src/box/sql/mem.h | 15 +-- src/box/sql/vdbe.c | 4 +- 3 files changed, 133 insertions(+), 188 deletions(-) diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c index 262f48aca..559bf6121 100644 --- a/src/box/sql/mem.c +++ b/src/box/sql/mem.c @@ -943,6 +943,134 @@ mem_convert_to_string(struct Mem *mem) return -1; } +static inline int +mem_convert_varstring_to_unsigned(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_unsigned(mem, (uint64_t)i); + return 0; +} + +static inline int +mem_convert_boolean_to_unsigned(struct Mem *mem) +{ + mem_set_unsigned(mem, (uint64_t)mem->u.b); + return 0; +} + +static inline int +mem_convert_string_to_boolean(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_boolean(mem, b); + return 0; +} + +static inline int +mem_convert_integer_to_boolean(struct Mem *mem) +{ + mem_set_boolean(mem, mem->u.u != 0); + return 0; +} + +static inline int +mem_convert_double_to_boolean(struct Mem *mem) +{ + mem_set_boolean(mem, mem->u.r != 0); + return 0; +} + +static inline int +mem_convert_string_to_varbinary(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_explicit_cast(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 mem_convert_varstring_to_unsigned(mem); + if ((mem->flags & MEM_Real) != 0) + return mem_convert_double_to_integer(mem); + if ((mem->flags & MEM_Bool) != 0) + return mem_convert_boolean_to_unsigned(mem); + return -1; + case FIELD_TYPE_STRING: + return mem_convert_to_string(mem); + case FIELD_TYPE_DOUBLE: + return mem_convert_to_double(mem); + case FIELD_TYPE_INTEGER: + return mem_convert_to_integer(mem); + case FIELD_TYPE_BOOLEAN: + if ((mem->flags & MEM_Bool) != 0) + return 0; + if ((mem->flags & (MEM_UInt | MEM_Int)) != 0) + return mem_convert_integer_to_boolean(mem); + if ((mem->flags & MEM_Str) != 0) + return mem_convert_string_to_boolean(mem); + if ((mem->flags & MEM_Real) != 0) + return mem_convert_double_to_boolean(mem); + return -1; + case FIELD_TYPE_VARBINARY: + if (mem_is_binary(mem)) + return 0; + if (mem_is_string(mem)) + return mem_convert_string_to_varbinary(mem); + return -1; + case FIELD_TYPE_NUMBER: + return mem_convert_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) { @@ -1583,42 +1711,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 @@ -2000,109 +2092,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_boolean(pMem, pMem->u.i); - return 0; - } - if ((pMem->flags & MEM_UInt) != 0) { - mem_set_boolean(pMem, pMem->u.u); - return 0; - } - if ((pMem->flags & MEM_Real) != 0) { - mem_set_boolean(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_boolean(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_integer(pMem, val, is_neg); - return 0; - } - if ((pMem->flags & MEM_Bool) != 0) { - mem_set_integer(pMem, (int64_t)pMem->u.b, false); - return 0; - } - if ((pMem->flags & MEM_Real) != 0) { - double d; - if (sqlVdbeRealValue(pMem, &d) != 0) - return -1; - if (d < (double)INT64_MAX && d >= (double)INT64_MIN) { - mem_set_integer(pMem, d, d <= -1); - return 0; - } - if (d >= (double)INT64_MAX && d < (double)UINT64_MAX) { - mem_set_unsigned(pMem, d); - return 0; - } - return -1; - } - if (type == FIELD_TYPE_UNSIGNED && - (pMem->flags & MEM_UInt) == 0) - return -1; - return 0; - case FIELD_TYPE_DOUBLE: - return mem_convert_to_double(pMem); - case FIELD_TYPE_NUMBER: - return mem_convert_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_string0(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. */ @@ -2255,41 +2244,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_set_unsigned(mem, (uint64_t) d); - return 0; -} - -int -mem_convert_to_numeric(struct Mem *mem, enum field_type type) -{ - assert(mem_is_number(mem) && sql_type_is_numeric(type)); - assert(type != FIELD_TYPE_NUMBER); - if (type == FIELD_TYPE_DOUBLE) - return mem_convert_to_double(mem); - if (type == FIELD_TYPE_UNSIGNED) - return mem_convert_to_unsigned(mem); - assert(type == FIELD_TYPE_INTEGER); - return mem_convert_to_integer(mem); -} - /* * Make sure pMem->z points to a writable allocation of at least * min(n,32) bytes. diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h index d370b448b..922dad272 100644 --- a/src/box/sql/mem.h +++ b/src/box/sql/mem.h @@ -336,6 +336,9 @@ mem_convert_to_string(struct Mem *mem); int mem_convert_to_string0(struct Mem *mem); +int +mem_explicit_cast(struct Mem *mem, enum field_type type); + /** * Simple type to str convertor. It is used to simplify * error reporting. @@ -386,7 +389,6 @@ registerTrace(int iReg, Mem *p); # define memAboutToChange(P,M) #endif -int sqlVdbeMemCast(struct Mem *, enum field_type type); int sqlVdbeMemNulTerminate(struct Mem *); int sqlVdbeMemExpandBlob(struct Mem *); #define ExpandBlob(P) (mem_is_zeroblob(P)? sqlVdbeMemExpandBlob(P) : 0) @@ -429,17 +431,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 sqlVdbeMemGrow(struct Mem * pMem, int n, int preserve); diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 6799cc9aa..a567f69bd 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -1389,7 +1389,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_explicit_cast(pIn1, pOp->p2); /* * SCALAR is not type itself, but rather an aggregation * of types. Hence, cast to this type shouldn't change @@ -2009,7 +2009,7 @@ case OP_ApplyType: { if (!mem_is_number(pIn1)) goto type_mismatch; /* Try to convert numeric-to-numeric. */ - if (mem_convert_to_numeric(pIn1, type) != 0) + if (mem_explicit_cast(pIn1, type) != 0) goto type_mismatch; } pIn1++; -- 2.25.1