From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 84590310CC for ; Fri, 7 Jun 2019 11:37:51 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qjwvrNpkWZEn for ; Fri, 7 Jun 2019 11:37:51 -0400 (EDT) Received: from smtpng2.m.smailru.net (smtpng2.m.smailru.net [94.100.179.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id D1B3031038 for ; Fri, 7 Jun 2019 11:37:50 -0400 (EDT) From: Nikita Pettik Subject: [tarantool-patches] [PATCH 2/6] sql: separate VDBE memory holding positive and negative ints Date: Fri, 7 Jun 2019 18:37:42 +0300 Message-Id: <0e009036b6c6ce9e2d1f2ff66063291cb60e1387.1559919361.git.korablev@tarantool.org> In-Reply-To: References: In-Reply-To: References: Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-Help: List-Unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-Subscribe: List-Owner: List-post: List-Archive: To: tarantool-patches@freelists.org Cc: v.shpilevoy@tarantool.org, Nikita Pettik As it was stated in the previous commit message, we are going to support operations on unsigned values. Since unsigned and signed integers have different memory representations, to provide correct results of arithmetic operations we should be able to tell whether value is signed or not. This patch introduces new type of value placed in VDBE memory cell - MEM_UInt. This flag means that value is integer and greater than zero, hence can be fitted in range [0, 2^64 - 1]. Such approach would make further replacing MEM_* flags with MP_ format types quite easy: during decoding and encoding msgpack we assume that negative integers have MP_INT type and positive - MP_UINT. We also add and refactor several auxiliary helpers to operate on integers. Note that current changes don't add ability to operate on unsigned integers - it is still unavailable. Needed for #3810 Needed for #4015 --- src/box/execute.c | 19 ++-- src/box/lua/lua_sql.c | 3 + src/box/sql/func.c | 14 ++- src/box/sql/vdbe.c | 199 +++++++++++++++++++++++------------------ src/box/sql/vdbeInt.h | 17 +++- src/box/sql/vdbeapi.c | 13 +-- src/box/sql/vdbeaux.c | 46 +++++++--- src/box/sql/vdbemem.c | 131 ++++++++++++++------------- test/sql-tap/position.test.lua | 2 +- test/sql/types.result | 12 +-- 10 files changed, 265 insertions(+), 191 deletions(-) diff --git a/src/box/execute.c b/src/box/execute.c index a3d4a92b8..f5aead391 100644 --- a/src/box/execute.c +++ b/src/box/execute.c @@ -138,17 +138,20 @@ sql_column_to_messagepack(struct sql_stmt *stmt, int i, switch (type) { case MP_INT: { int64_t n = sql_column_int64(stmt, i); - if (n >= 0) - size = mp_sizeof_uint(n); - else - size = mp_sizeof_int(n); + size = mp_sizeof_int(n); char *pos = (char *) region_alloc(region, size); if (pos == NULL) goto oom; - if (n >= 0) - mp_encode_uint(pos, n); - else - mp_encode_int(pos, n); + mp_encode_int(pos, n); + break; + } + case MP_UINT: { + uint64_t n = sql_column_int64(stmt, i); + size = mp_sizeof_uint(n); + char *pos = (char *) region_alloc(region, size); + if (pos == NULL) + goto oom; + mp_encode_uint(pos, n); break; } case MP_DOUBLE: { diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c index 36b75ff08..59ea260bf 100644 --- a/src/box/lua/lua_sql.c +++ b/src/box/lua/lua_sql.c @@ -60,6 +60,9 @@ lua_sql_call(sql_context *pCtx, int nVal, sql_value **apVal) { case MP_INT: luaL_pushint64(L, sql_value_int64(param)); break; + case MP_UINT: + luaL_pushuint64(L, sql_value_int64(param)); + break; case MP_DOUBLE: lua_pushnumber(L, sql_value_double(param)); break; diff --git a/src/box/sql/func.c b/src/box/sql/func.c index bb7405e68..f4c1cbcca 100644 --- a/src/box/sql/func.c +++ b/src/box/sql/func.c @@ -111,6 +111,7 @@ typeofFunc(sql_context * context, int NotUsed, sql_value ** argv) UNUSED_PARAMETER(NotUsed); switch (sql_value_type(argv[0])) { case MP_INT: + case MP_UINT: z = "integer"; break; case MP_STR: @@ -145,6 +146,7 @@ lengthFunc(sql_context * context, int argc, sql_value ** argv) switch (sql_value_type(argv[0])) { case MP_BIN: case MP_INT: + case MP_UINT: case MP_DOUBLE:{ sql_result_int(context, sql_value_bytes(argv[0])); @@ -177,6 +179,7 @@ absFunc(sql_context * context, int argc, sql_value ** argv) assert(argc == 1); UNUSED_PARAMETER(argc); switch (sql_value_type(argv[0])) { + case MP_UINT: case MP_INT:{ i64 iVal = sql_value_int64(argv[0]); if (iVal < 0) { @@ -1041,6 +1044,7 @@ quoteFunc(sql_context * context, int argc, sql_value ** argv) SQL_TRANSIENT); break; } + case MP_UINT: case MP_INT:{ sql_result_value(context, argv[0]); break; @@ -1442,7 +1446,8 @@ trim_func_two_args(struct sql_context *context, int argc, sql_value **argv) return; int input_str_sz = sql_value_bytes(argv[1]); - if (sql_value_type(argv[0]) == MP_INT) { + if (sql_value_type(argv[0]) == MP_INT || + sql_value_type(argv[0]) == MP_UINT) { uint8_t len_one = 1; trim_procedure(context, sql_value_int(argv[0]), (const unsigned char *) " ", &len_one, 1, @@ -1473,7 +1478,8 @@ trim_func_three_args(struct sql_context *context, int argc, sql_value **argv) assert(argc == 3); (void) argc; - assert(sql_value_type(argv[0]) == MP_INT); + assert(sql_value_type(argv[0]) == MP_INT || + sql_value_type(argv[0]) == MP_UINT); const unsigned char *input_str, *trim_set; if ((input_str = sql_value_text(argv[2])) == NULL || (trim_set = sql_value_text(argv[1])) == NULL) @@ -1601,7 +1607,7 @@ sum_step(struct sql_context *context, int argc, sql_value **argv) int type = sql_value_type(argv[0]); if (type == MP_NIL || p == NULL) return; - if (type != MP_DOUBLE && type != MP_INT) { + if (type != MP_DOUBLE && type != MP_INT && type != MP_UINT) { if (mem_apply_numeric_type(argv[0]) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_text(argv[0]), "number"); @@ -1612,7 +1618,7 @@ sum_step(struct sql_context *context, int argc, sql_value **argv) type = sql_value_type(argv[0]); } p->cnt++; - if (type == MP_INT) { + if (type == MP_INT || type == MP_UINT) { int64_t v = sql_value_int64(argv[0]); p->rSum += v; if ((p->approx | p->overflow) == 0 && diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 6ecdb26fc..d141397a0 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -276,13 +276,12 @@ allocateCursor( int mem_apply_numeric_type(struct Mem *record) { - if ((record->flags & (MEM_Str | MEM_Int | MEM_Real)) != MEM_Str) + if ((record->flags & (MEM_Str | MEM_Int | MEM_Real | MEM_UInt)) != MEM_Str) return -1; int64_t integer_value; bool is_neg; if (sql_atoi64(record->z, &integer_value, &is_neg, record->n) == 0) { - record->u.i = integer_value; - MemSetTypeFlag(record, MEM_Int); + mem_set_int(record, integer_value, is_neg); return 0; } double float_value; @@ -325,12 +324,12 @@ mem_apply_type(struct Mem *record, enum field_type type) case FIELD_TYPE_UNSIGNED: if ((record->flags & MEM_Int) == MEM_Int) return 0; + if ((record->flags & MEM_UInt) == MEM_UInt) + return 0; if ((record->flags & MEM_Real) == MEM_Real) { int64_t i = (int64_t) record->u.r; - if (i == record->u.r) { - record->u.i = i; - MemSetTypeFlag(record, MEM_Int); - } + if (i == record->u.r) + mem_set_int(record, i, i < 0); return 0; } return sqlVdbeMemIntegerify(record, false); @@ -339,7 +338,7 @@ mem_apply_type(struct Mem *record, enum field_type type) return 0; return -1; case FIELD_TYPE_NUMBER: - if ((record->flags & (MEM_Real | MEM_Int)) != 0) + if ((record->flags & (MEM_Real | MEM_Int | MEM_UInt)) != 0) return 0; return sqlVdbeMemRealify(record); case FIELD_TYPE_STRING: @@ -349,10 +348,10 @@ mem_apply_type(struct Mem *record, enum field_type type) * NULL do not get converted). */ if ((record->flags & MEM_Str) == 0) { - if ((record->flags & (MEM_Real | MEM_Int))) + if ((record->flags & (MEM_Real | MEM_Int | MEM_UInt))) sqlVdbeMemStringify(record, 1); } - record->flags &= ~(MEM_Real | MEM_Int); + record->flags &= ~(MEM_Real | MEM_Int | MEM_UInt); return 0; case FIELD_TYPE_SCALAR: return 0; @@ -381,13 +380,13 @@ sql_value_apply_type( */ static u16 SQL_NOINLINE computeNumericType(Mem *pMem) { - assert((pMem->flags & (MEM_Int|MEM_Real))==0); + assert((pMem->flags & (MEM_Int | MEM_UInt | MEM_Real))==0); assert((pMem->flags & (MEM_Str|MEM_Blob))!=0); if (sqlAtoF(pMem->z, &pMem->u.r, pMem->n)==0) return 0; bool is_neg; if (sql_atoi64(pMem->z, (int64_t *)&pMem->u.i, &is_neg, pMem->n)==SQL_OK) - return MEM_Int; + return is_neg ? MEM_Int : MEM_UInt; return MEM_Real; } @@ -400,8 +399,8 @@ static u16 SQL_NOINLINE computeNumericType(Mem *pMem) */ static u16 numericType(Mem *pMem) { - if (pMem->flags & (MEM_Int|MEM_Real)) { - return pMem->flags & (MEM_Int|MEM_Real); + if (pMem->flags & (MEM_Int| MEM_UInt | MEM_Real)) { + return pMem->flags & (MEM_Int | MEM_UInt | MEM_Real); } if (pMem->flags & (MEM_Str|MEM_Blob)) { return computeNumericType(pMem); @@ -506,6 +505,8 @@ memTracePrint(Mem *p) printf(" si:%lld", p->u.i); } else if (p->flags & MEM_Int) { printf(" i:%lld", p->u.i); + } else if (p->flags & MEM_UInt) { + printf(" u:%"PRIu64"", p->u.u); } else if (p->flags & MEM_Real) { printf(" r:%g", p->u.r); } else if (p->flags & MEM_Bool) { @@ -604,6 +605,8 @@ mem_type_to_str(const struct Mem *p) return "TEXT"; case MEM_Int: return "INTEGER"; + case MEM_UInt: + return "UNSIGNED"; case MEM_Real: return "REAL"; case MEM_Blob: @@ -858,8 +861,9 @@ case OP_Gosub: { /* jump */ pIn1 = &aMem[pOp->p1]; assert(VdbeMemDynamic(pIn1)==0); memAboutToChange(p, pIn1); - pIn1->flags = MEM_Int; - pIn1->u.i = (int)(pOp-aOp); + pIn1->flags = MEM_UInt; + assert((pOp-aOp) >= 0); + pIn1->u.u = (int)(pOp-aOp); REGISTER_TRACE(pOp->p1, pIn1); /* Most jump operations do a goto to this spot in order to update @@ -877,8 +881,8 @@ case OP_Gosub: { /* jump */ */ case OP_Return: { /* in1 */ pIn1 = &aMem[pOp->p1]; - assert(pIn1->flags==MEM_Int); - pOp = &aOp[pIn1->u.i]; + assert(pIn1->flags==MEM_UInt); + pOp = &aOp[pIn1->u.u]; pIn1->flags = MEM_Undefined; break; } @@ -900,8 +904,8 @@ case OP_InitCoroutine: { /* jump */ assert(pOp->p3>=0 && pOp->p3nOp); pOut = &aMem[pOp->p1]; assert(!VdbeMemDynamic(pOut)); - pOut->u.i = pOp->p3 - 1; - pOut->flags = MEM_Int; + pOut->u.u = pOp->p3 - 1; + pOut->flags = MEM_UInt; if (pOp->p2) goto jump_to_p2; break; } @@ -917,9 +921,9 @@ case OP_InitCoroutine: { /* jump */ case OP_EndCoroutine: { /* in1 */ VdbeOp *pCaller; pIn1 = &aMem[pOp->p1]; - assert(pIn1->flags==MEM_Int); - assert(pIn1->u.i>=0 && pIn1->u.inOp); - pCaller = &aOp[pIn1->u.i]; + assert(pIn1->flags == MEM_UInt); + assert(pIn1->u.u < (uint64_t) p->nOp); + pCaller = &aOp[pIn1->u.u]; assert(pCaller->opcode==OP_Yield); assert(pCaller->p2>=0 && pCaller->p2nOp); pOp = &aOp[pCaller->p2 - 1]; @@ -944,9 +948,9 @@ case OP_Yield: { /* in1, jump */ int pcDest; pIn1 = &aMem[pOp->p1]; assert(VdbeMemDynamic(pIn1)==0); - pIn1->flags = MEM_Int; - pcDest = (int)pIn1->u.i; - pIn1->u.i = (int)(pOp - aOp); + pIn1->flags = MEM_UInt; + pcDest = (int)pIn1->u.u; + pIn1->u.u = (int)(pOp - aOp); REGISTER_TRACE(pOp->p1, pIn1); pOp = &aOp[pcDest]; break; @@ -1074,7 +1078,12 @@ case OP_Halt: { */ case OP_Integer: { /* out2 */ pOut = out2Prerelease(p, pOp); - pOut->u.i = pOp->p1; + if (pOp->p1 < 0) { + pOut->u.i = pOp->p1; + } else { + pOut->u.u = pOp->p1; + MemSetTypeFlag(pOut, MEM_UInt); + } break; } @@ -1099,7 +1108,7 @@ case OP_Bool: { /* out2 */ case OP_Int64: { /* out2 */ pOut = out2Prerelease(p, pOp); assert(pOp->p4.pI64!=0); - pOut->u.i = *pOp->p4.pI64; + mem_set_int(pOut, *pOp->p4.pI64, *pOp->p4.pI64 < 0); break; } @@ -1187,8 +1196,7 @@ case OP_NextAutoincValue: { } pOut = out2Prerelease(p, pOp); - pOut->flags = MEM_Int; - pOut->u.i = value; + mem_set_int(pOut, value, value < 0); break; } @@ -1384,9 +1392,9 @@ case OP_SCopy: { /* out2 */ */ case OP_IntCopy: { /* out2 */ pIn1 = &aMem[pOp->p1]; - assert((pIn1->flags & MEM_Int)!=0); + assert((pIn1->flags & (MEM_Int | MEM_UInt))!=0); pOut = &aMem[pOp->p2]; - sqlVdbeMemSetInt64(pOut, pIn1->u.i); + mem_set_int(pOut, pIn1->u.i, pIn1->flags & MEM_Int); break; } @@ -1609,7 +1617,8 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ pOut = &aMem[pOp->p3]; flags = pIn1->flags | pIn2->flags; if ((flags & MEM_Null)!=0) goto arithmetic_result_is_null; - if ((type1 & type2 & MEM_Int)!=0) { + if ((type1 & (MEM_Int | MEM_UInt)) !=0 && + (type2 & (MEM_Int | MEM_UInt)) !=0) { iA = pIn1->u.i; iB = pIn2->u.i; bIntint = 1; @@ -1632,8 +1641,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ break; } } - pOut->u.i = iB; - MemSetTypeFlag(pOut, MEM_Int); + mem_set_int(pOut, iB, iB < 0); } else { bIntint = 0; if (sqlVdbeRealValue(pIn1, &rA) != 0) { @@ -1874,13 +1882,14 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ sqlVdbeMemSetNull(pOut); break; } - if (sqlVdbeIntValue(pIn2, (int64_t *) &iA) != 0) { + bool unused; + if (sqlVdbeIntValue(pIn2, &iA, &unused) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_text(pIn2), "integer"); rc = SQL_TARANTOOL_ERROR; goto abort_due_to_error; } - if (sqlVdbeIntValue(pIn1, (int64_t *) &iB) != 0) { + if (sqlVdbeIntValue(pIn1, &iB, &unused) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_text(pIn1), "integer"); rc = SQL_TARANTOOL_ERROR; @@ -1915,8 +1924,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ memcpy(&iA, &uA, sizeof(iA)); } } - pOut->u.i = iA; - MemSetTypeFlag(pOut, MEM_Int); + mem_set_int(pOut, iA, iA < 0); break; } @@ -1924,15 +1932,14 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ * Synopsis: r[P1]=r[P1]+P2 * * Add the constant P2 to the value in register P1. - * The result is always an integer. - * - * To force any register to be an integer, just add 0. + * Content of register P1 and value P2 are assumed to be + * unsigned. */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); - assert((pIn1->flags & MEM_Int) != 0); - pIn1->u.i += pOp->p2; + assert((pIn1->flags & MEM_UInt) != 0 && pOp->p2 >= 0); + pIn1->u.u += pOp->p2; break; } @@ -1945,10 +1952,9 @@ case OP_AddImm: { /* in1 */ */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - if ((pIn1->flags & MEM_Int)==0) { + if ((pIn1->flags & (MEM_Int | MEM_UInt)) == 0) { mem_apply_type(pIn1, FIELD_TYPE_INTEGER); - VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2); - if ((pIn1->flags & MEM_Int)==0) { + if ((pIn1->flags & (MEM_Int | MEM_UInt))== 0) { if (pOp->p2==0) { rc = SQL_MISMATCH; goto abort_due_to_error; @@ -1957,7 +1963,6 @@ case OP_MustBeInt: { /* jump, in1 */ } } } - MemSetTypeFlag(pIn1, MEM_Int); break; } @@ -1972,7 +1977,7 @@ case OP_MustBeInt: { /* jump, in1 */ */ case OP_Realify: { /* in1 */ pIn1 = &aMem[pOp->p1]; - if (pIn1->flags & MEM_Int) { + if ((pIn1->flags & (MEM_Int | MEM_UInt)) != 0) { sqlVdbeMemRealify(pIn1); } break; @@ -2166,12 +2171,12 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ enum field_type type = pOp->p5 & FIELD_TYPE_MASK; if (sql_type_is_numeric(type)) { if ((flags1 | flags3)&MEM_Str) { - if ((flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str) { + if ((flags1 & (MEM_Int|MEM_UInt|MEM_Real|MEM_Str))==MEM_Str) { mem_apply_numeric_type(pIn1); testcase( flags3!=pIn3->flags); /* Possible if pIn1==pIn3 */ flags3 = pIn3->flags; } - if ((flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str) { + if ((flags3 & (MEM_Int|MEM_UInt|MEM_Real|MEM_Str))==MEM_Str) { if (mem_apply_numeric_type(pIn3) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, @@ -2186,14 +2191,14 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ /* Handle the common case of integer comparison here, as an * optimization, to avoid a call to sqlMemCompare() */ - if ((pIn1->flags & pIn3->flags & MEM_Int)!=0) { + if ((pIn1->flags & pIn3->flags & (MEM_Int | MEM_UInt))!=0) { if (pIn3->u.i > pIn1->u.i) { res = +1; goto compare_op; } if (pIn3->u.i < pIn1->u.i) { res = -1; goto compare_op; } res = 0; goto compare_op; } } else if (type == FIELD_TYPE_STRING) { - if ((flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_Real))!=0) { + if ((flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_UInt|MEM_Real))!=0) { testcase( pIn1->flags & MEM_Int); testcase( pIn1->flags & MEM_Real); sqlVdbeMemStringify(pIn1, 1); @@ -2201,7 +2206,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); assert(pIn1!=pIn3); } - if ((flags3 & MEM_Str)==0 && (flags3 & (MEM_Int|MEM_Real))!=0) { + if ((flags3 & MEM_Str)==0 && (flags3 & (MEM_Int|MEM_UInt|MEM_Real))!=0) { testcase( pIn3->flags & MEM_Int); testcase( pIn3->flags & MEM_Real); sqlVdbeMemStringify(pIn3, 1); @@ -2485,14 +2490,14 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ sqlVdbeMemSetNull(pOut); if ((pIn1->flags & MEM_Null)==0) { int64_t i; - if (sqlVdbeIntValue(pIn1, &i) != 0) { + bool is_neg; + if (sqlVdbeIntValue(pIn1, &i, &is_neg) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_text(pIn1), "integer"); rc = SQL_TARANTOOL_ERROR; goto abort_due_to_error; } - pOut->flags = MEM_Int; - pOut->u.i = ~i; + mem_set_int(pOut, ~i, ~i < 0); } break; } @@ -2750,7 +2755,7 @@ case OP_Column: { pDest->flags = MEM_Blob|MEM_Ephem|MEM_Subtype; pDest->subtype = SQL_SUBTYPE_MSGPACK; } - if ((pDest->flags & MEM_Int) != 0 && + if ((pDest->flags & (MEM_Int|MEM_UInt)) != 0 && pC->eCurType == CURTYPE_TARANTOOL) { enum field_type f = FIELD_TYPE_ANY; /* @@ -2954,7 +2959,9 @@ case OP_Count: { /* out2 */ } if (rc) goto abort_due_to_error; pOut = out2Prerelease(p, pOp); - pOut->u.i = nEntry; + assert(nEntry >= 0); + pOut->u.u = nEntry; + pOut->flags = MEM_UInt; break; } @@ -3499,6 +3506,7 @@ case OP_SeekGT: { /* jump, in3 */ #endif iKey = 0; reg_ipk = pOp->p5; + bool is_neg = false; if (reg_ipk > 0) { @@ -3507,12 +3515,16 @@ case OP_SeekGT: { /* jump, in3 */ * the seek, so convert it. */ pIn3 = &aMem[reg_ipk]; - if ((pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str) { + if ((pIn3->flags & (MEM_Int|MEM_UInt|MEM_Real|MEM_Str))==MEM_Str) { mem_apply_numeric_type(pIn3); } int64_t i; if ((pIn3->flags & MEM_Int) == MEM_Int) { i = pIn3->u.i; + is_neg = true; + } else if ((pIn3->flags & MEM_UInt) == MEM_UInt) { + i = pIn3->u.u; + is_neg = false; } else if ((pIn3->flags & MEM_Real) == MEM_Real) { if (pIn3->u.r > INT64_MAX) i = INT64_MAX; @@ -3520,6 +3532,7 @@ case OP_SeekGT: { /* jump, in3 */ i = INT64_MIN; else i = pIn3->u.r; + is_neg = i < 0; } else { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_text(pIn3), "integer"); @@ -3531,7 +3544,7 @@ case OP_SeekGT: { /* jump, in3 */ /* If the P3 value could not be converted into an integer without * loss of information, then special processing is required... */ - if ((pIn3->flags & MEM_Int)==0) { + if ((pIn3->flags & (MEM_Int | MEM_UInt)) == 0) { if ((pIn3->flags & MEM_Real)==0) { /* If the P3 value cannot be converted into any kind of a number, * then the seek is not possible, so jump to P2 @@ -3587,10 +3600,8 @@ case OP_SeekGT: { /* jump, in3 */ r.key_def = pC->key_def; r.nField = (u16)nField; - if (reg_ipk > 0) { - aMem[reg_ipk].u.i = iKey; - aMem[reg_ipk].flags = MEM_Int; - } + if (reg_ipk > 0) + mem_set_int(&aMem[reg_ipk], iKey, is_neg); r.default_rc = ((1 & (oc - OP_SeekLT)) ? -1 : +1); assert(oc!=OP_SeekGT || r.default_rc==-1); @@ -3801,7 +3812,10 @@ case OP_Sequence: { /* out2 */ assert(pOp->p1>=0 && pOp->p1nCursor); assert(p->apCsr[pOp->p1]!=0); pOut = out2Prerelease(p, pOp); - pOut->u.i = p->apCsr[pOp->p1]->seqCount++; + int64_t seq_val = p->apCsr[pOp->p1]->seqCount++; + assert(seq_val >= 0); + pOut->u.u = seq_val; + pOut->flags = MEM_UInt; break; } @@ -3814,10 +3828,10 @@ case OP_Sequence: { /* out2 */ */ case OP_NextSequenceId: { pOut = &aMem[pOp->p2]; - tarantoolSqlNextSeqId((uint64_t *) &pOut->u.i); - - pOut->u.i += 1; - pOut->flags = MEM_Int; + int64_t id; + tarantoolSqlNextSeqId((uint64_t *) &id); + id++; + mem_set_int(pOut, id, id < 0); break; } @@ -3850,8 +3864,8 @@ case OP_NextIdEphemeral: { goto abort_due_to_error; } pOut = &aMem[pOp->p2]; - pOut->u.i = rowid; - pOut->flags = MEM_Int; + pOut->u.u = rowid; + pOut->flags = MEM_UInt; break; } @@ -3883,12 +3897,11 @@ case OP_FCopy: { /* out2 */ /* Flag is set and register is NULL -> do nothing */ } else { assert(memIsValid(pIn1)); - assert(pIn1->flags & MEM_Int); + assert((pIn1->flags & (MEM_Int | MEM_UInt)) != 0); pOut = &aMem[pOp->p2]; - MemSetTypeFlag(pOut, MEM_Int); - - pOut->u.i = pIn1->u.i; + mem_set_int(pOut, pIn1->u.i, pIn1->flags == MEM_UInt ? + false : true); } break; } @@ -5079,10 +5092,17 @@ case OP_FkIfZero: { /* jump */ */ case OP_IfPos: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - assert(pIn1->flags&MEM_Int); - VdbeBranchTaken( pIn1->u.i>0, 2); - if (pIn1->u.i>0) { - pIn1->u.i -= pOp->p3; + assert((pIn1->flags & (MEM_Int | MEM_UInt)) != 0); + if ((pIn1->flags & MEM_UInt) != 0 && pIn1->u.u != 0) { + assert(pOp->p3 >= 0); + uint64_t res = pIn1->u.u - (uint64_t) pOp->p3; + /* + * To not bother setting integer flag in case + * result of subtraction is negative, just + * use saturated arithmetic. + */ + res &= -(res <= pIn1->u.u); + pIn1->u.u = res; goto jump_to_p2; } break; @@ -5111,10 +5131,10 @@ case OP_OffsetLimit: { /* in1, out2, in3 */ pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; pOut = out2Prerelease(p, pOp); - assert(pIn1->flags & MEM_Int); - assert(pIn3->flags & MEM_Int); + assert(pIn1->flags & (MEM_Int | MEM_UInt)); + assert(pIn3->flags & (MEM_Int | MEM_UInt)); x = pIn1->u.i; - if (x<=0 || sqlAddInt64(&x, pIn3->u.i>0?pIn3->u.i:0)) { + if (x<=0 || sqlAddInt64(&x, pIn3->u.i > 0 ? pIn3->u.i : 0)) { /* If the LIMIT is less than or equal to zero, loop forever. This * is documented. But also, if the LIMIT+OFFSET exceeds 2^63 then * also loop forever. This is undocumented. In fact, one could argue @@ -5124,8 +5144,9 @@ case OP_OffsetLimit: { /* in1, out2, in3 */ * looping forever is a reasonable approximation. */ pOut->u.i = -1; + pOut->flags = MEM_Int; } else { - pOut->u.i = x; + mem_set_int(pOut, x, x < 0); } break; } @@ -5140,7 +5161,7 @@ case OP_OffsetLimit: { /* in1, out2, in3 */ */ case OP_IfNotZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - assert(pIn1->flags&MEM_Int); + assert(pIn1->flags&(MEM_Int|MEM_UInt)); VdbeBranchTaken(pIn1->u.i<0, 2); if (pIn1->u.i) { if (pIn1->u.i>0) pIn1->u.i--; @@ -5157,7 +5178,7 @@ case OP_IfNotZero: { /* jump, in1 */ */ case OP_DecrJumpZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - assert(pIn1->flags&MEM_Int); + assert((pIn1->flags&(MEM_Int|MEM_UInt)) != 0); if (pIn1->u.i>SMALLEST_INT64) pIn1->u.i--; VdbeBranchTaken(pIn1->u.i==0, 2); if (pIn1->u.i==0) goto jump_to_p2; @@ -5396,12 +5417,12 @@ case OP_Init: { /* jump */ case OP_IncMaxid: { assert(pOp->p1 > 0); pOut = &aMem[pOp->p1]; - - rc = tarantoolsqlIncrementMaxid((uint64_t*) &pOut->u.i); + int64_t id; + rc = tarantoolsqlIncrementMaxid((uint64_t*) &id); if (rc!=SQL_OK) { goto abort_due_to_error; } - pOut->flags = MEM_Int; + mem_set_int(pOut, id, id < 0); break; } diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h index a3100e513..abed46486 100644 --- a/src/box/sql/vdbeInt.h +++ b/src/box/sql/vdbeInt.h @@ -183,6 +183,7 @@ struct Mem { union MemValue { double r; /* Real value used when MEM_Real is set in flags */ i64 i; /* Integer value used when MEM_Int is set in flags */ + uint64_t u; /* Unsigned integer used when MEM_UInt is set. */ bool b; /* Boolean value used when MEM_Bool is set in flags */ int nZero; /* Used when bit MEM_Zero is set in flags */ void *p; /* Generic pointer */ @@ -230,7 +231,7 @@ struct Mem { #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_Bool 0x0020 /* Value is a bool */ -#define MEM_Ptr 0x0040 /* Value is a generic pointer */ +#define MEM_UInt 0x0040 /* Value is a unsigned integer */ #define MEM_Frame 0x0080 /* Value is a VdbeFrame object */ #define MEM_Undefined 0x0100 /* Value is undefined */ #define MEM_Cleared 0x0200 /* NULL set by OP_Null, not from data */ @@ -248,6 +249,7 @@ struct Mem { #define MEM_Agg 0x4000 /* Mem.z points to an agg function context */ #define MEM_Zero 0x8000 /* Mem.i contains count of 0s appended to blob */ #define MEM_Subtype 0x10000 /* Mem.eSubtype is valid */ +#define MEM_Ptr 0x20000 /* Value is a generic pointer */ #ifdef SQL_OMIT_INCRBLOB #undef MEM_Zero #define MEM_Zero 0x0000 @@ -259,9 +261,14 @@ struct Mem { * auxiliary flags. */ enum { - MEM_PURE_TYPE_MASK = 0x3f + MEM_PURE_TYPE_MASK = 0x7f }; +static_assert((int) MEM_PURE_TYPE_MASK == (MEM_Null | MEM_Str | MEM_Int | + MEM_Real | MEM_Blob | MEM_Bool | + MEM_UInt), + "value of type mask must consist of corresponding to memory type bits"); + /** * Simple type to str convertor. It is used to simplify * error reporting. @@ -499,13 +506,17 @@ void sqlVdbeMemSetInt64(Mem *, i64); void mem_set_bool(struct Mem *mem, bool value); +void +mem_set_int(struct Mem *mem, int64_t value, bool is_neg); + void sqlVdbeMemSetDouble(Mem *, double); void sqlVdbeMemInit(Mem *, sql *, u32); void sqlVdbeMemSetNull(Mem *); void sqlVdbeMemSetZeroBlob(Mem *, int); int sqlVdbeMemMakeWriteable(Mem *); int sqlVdbeMemStringify(Mem *, u8); -int sqlVdbeIntValue(Mem *, int64_t *); +int sqlVdbeIntValue(Mem *, int64_t *, bool *is_neg); + int sqlVdbeMemIntegerify(Mem *, bool is_forced); int sqlVdbeRealValue(Mem *, double *); diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c index e480ae720..393782c23 100644 --- a/src/box/sql/vdbeapi.c +++ b/src/box/sql/vdbeapi.c @@ -225,7 +225,8 @@ int sql_value_int(sql_value * pVal) { int64_t i = 0; - sqlVdbeIntValue((Mem *) pVal, &i); + bool is_neg; + sqlVdbeIntValue((Mem *) pVal, &i, &is_neg); return (int)i; } @@ -233,7 +234,8 @@ sql_int64 sql_value_int64(sql_value * pVal) { int64_t i = 0; - sqlVdbeIntValue((Mem *) pVal, &i); + bool is_neg; + sqlVdbeIntValue((Mem *) pVal, &i, &is_neg); return i; } @@ -258,6 +260,7 @@ sql_value_type(sql_value *pVal) { switch (pVal->flags & MEM_PURE_TYPE_MASK) { case MEM_Int: return MP_INT; + case MEM_UInt: return MP_UINT; case MEM_Real: return MP_DOUBLE; case MEM_Str: return MP_STR; case MEM_Blob: return MP_BIN; @@ -385,7 +388,7 @@ sql_result_error(sql_context * pCtx, const char *z, int n) void sql_result_int(sql_context * pCtx, int iVal) { - sqlVdbeMemSetInt64(pCtx->pOut, (i64) iVal); + mem_set_int(pCtx->pOut, (i64) iVal, iVal < 0); } void @@ -397,7 +400,7 @@ sql_result_bool(struct sql_context *ctx, bool value) void sql_result_int64(sql_context * pCtx, i64 iVal) { - sqlVdbeMemSetInt64(pCtx->pOut, iVal); + mem_set_int(pCtx->pOut, iVal, iVal < 0); } void @@ -1376,7 +1379,7 @@ sql_bind_int64(sql_stmt * pStmt, int i, sql_int64 iValue) rc = vdbeUnbind(p, i); if (rc == SQL_OK) { rc = sql_bind_type(p, i, "INTEGER"); - sqlVdbeMemSetInt64(&p->aVar[i - 1], iValue); + mem_set_int(&p->aVar[i - 1], iValue, iValue < 0); } return rc; } diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c index 25d4cd759..5a71e1801 100644 --- a/src/box/sql/vdbeaux.c +++ b/src/box/sql/vdbeaux.c @@ -1369,6 +1369,8 @@ displayP4(Op * pOp, char *zTemp, int nTemp) zP4 = pMem->z; } else if (pMem->flags & MEM_Int) { sqlXPrintf(&x, "%lld", pMem->u.i); + } else if (pMem->flags & MEM_UInt) { + sqlXPrintf(&x, "%llu", pMem->u.u); } else if (pMem->flags & MEM_Real) { sqlXPrintf(&x, "%.16g", pMem->u.r); } else if (pMem->flags & MEM_Null) { @@ -1639,6 +1641,8 @@ sqlVdbeList(Vdbe * p) if (p->explain == 1) { pMem->flags = MEM_Int; pMem->u.i = i; /* Program counter */ + mem_set_int(pMem, i, i < 0); + pMem++; pMem->flags = MEM_Static | MEM_Str | MEM_Term; @@ -1672,16 +1676,13 @@ sqlVdbeList(Vdbe * p) } } - pMem->flags = MEM_Int; - pMem->u.i = pOp->p1; /* P1 */ + mem_set_int(pMem, pOp->p1, pOp->p1 < 0); pMem++; - pMem->flags = MEM_Int; - pMem->u.i = pOp->p2; /* P2 */ + mem_set_int(pMem, pOp->p2, pOp->p2 < 0); pMem++; - pMem->flags = MEM_Int; - pMem->u.i = pOp->p3; /* P3 */ + mem_set_int(pMem, pOp->p3, pOp->p3 < 0); pMem++; if (sqlVdbeMemClearAndResize(pMem, 256)) { @@ -3372,7 +3373,7 @@ sqlMemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pColl) /* At least one of the two values is a number */ - if (combined_flags & (MEM_Int | MEM_Real)) { + if (combined_flags & (MEM_Int | MEM_UInt | MEM_Real)) { if ((f1 & f2 & MEM_Int) != 0) { if (pMem1->u.i < pMem2->u.i) return -1; @@ -3380,6 +3381,13 @@ sqlMemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pColl) return +1; return 0; } + if ((f1 & f2 & MEM_UInt) != 0) { + if (pMem1->u.u < pMem2->u.u) + return -1; + if (pMem1->u.u > pMem2->u.u) + return +1; + return 0; + } if ((f1 & f2 & MEM_Real) != 0) { if (pMem1->u.r < pMem2->u.r) return -1; @@ -3395,10 +3403,23 @@ sqlMemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pColl) return -1; } } + if ((f1 & MEM_UInt) != 0) { + if ((f2 & MEM_Real) != 0) { + return sqlIntFloatCompare(pMem1->u.i, + pMem2->u.r); + } else if ((f2 & MEM_Int) != 0) { + return +1; + } else { + return -1; + } + } if ((f1 & MEM_Real) != 0) { if ((f2 & MEM_Int) != 0) { return -sqlIntFloatCompare(pMem2->u.i, pMem1->u.r); + } else if ((f2 & MEM_UInt) != 0) { + return -sqlIntFloatCompare(pMem2->u.u, + pMem1->u.r); } else { return -1; } @@ -3563,13 +3584,14 @@ sqlVdbeCompareMsgpack(const char **key1, mem1.u.r = (double)v; goto do_float; } - mem1.u.i = v; + mem1.u.u = v; goto do_int; } case MP_INT:{ mem1.u.i = mp_decode_int(&aKey1); do_int: - if (pKey2->flags & MEM_Int) { + if ((pKey2->flags & MEM_Int) || + (pKey2->flags & MEM_UInt)) { if (mem1.u.i < pKey2->u.i) { rc = -1; } else if (mem1.u.i > pKey2->u.i) { @@ -3590,7 +3612,7 @@ sqlVdbeCompareMsgpack(const char **key1, case MP_DOUBLE:{ mem1.u.r = mp_decode_double(&aKey1); do_float: - if (pKey2->flags & MEM_Int) { + if (pKey2->flags & MEM_Int || pKey2->flags & MEM_UInt) { rc = -sqlIntFloatCompare(pKey2->u.i, mem1.u.r); } else if (pKey2->flags & MEM_Real) { @@ -3718,8 +3740,8 @@ vdbe_decode_msgpack_into_mem(const char *buf, struct Mem *mem, uint32_t *len) "integer is overflowed"); return -1; } - mem->u.i = v; - mem->flags = MEM_Int; + mem->u.u = v; + mem->flags = MEM_UInt; break; } case MP_INT: { diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c index 3a361d066..25119ff16 100644 --- a/src/box/sql/vdbemem.c +++ b/src/box/sql/vdbemem.c @@ -67,6 +67,8 @@ sqlVdbeCheckMemInvariants(Mem * p) /* Cannot be both MEM_Int and MEM_Real at the same time */ assert((p->flags & (MEM_Int | MEM_Real)) != (MEM_Int | MEM_Real)); + /* Can't be both UInt and Int at the same time. */ + assert((p->flags & (MEM_Int | MEM_UInt)) != (MEM_Int | MEM_UInt)); /* The szMalloc field holds the correct memory allocation size */ assert(p->szMalloc == 0 @@ -289,7 +291,7 @@ sqlVdbeMemStringify(Mem * pMem, u8 bForce) return SQL_OK; assert(!(fg & MEM_Zero)); - assert(fg & (MEM_Int | MEM_Real | MEM_Bool)); + assert(fg & (MEM_Int | MEM_UInt | MEM_Real | MEM_Bool)); assert(EIGHT_BYTE_ALIGNMENT(pMem)); if (sqlVdbeMemClearAndResize(pMem, nByte)) { @@ -297,6 +299,8 @@ sqlVdbeMemStringify(Mem * pMem, u8 bForce) } if (fg & MEM_Int) { sql_snprintf(nByte, pMem->z, "%lld", pMem->u.i); + } else if ((fg & MEM_UInt) != 0) { + sql_snprintf(nByte, pMem->z, "%llu", pMem->u.u); } else if ((fg & MEM_Bool) != 0) { sql_snprintf(nByte, pMem->z, "%s", pMem->u.b ? "true" : "false"); } else { @@ -306,7 +310,7 @@ sqlVdbeMemStringify(Mem * pMem, u8 bForce) pMem->n = sqlStrlen30(pMem->z); pMem->flags |= MEM_Str | MEM_Term; if (bForce) - pMem->flags &= ~(MEM_Int | MEM_Real); + pMem->flags &= ~(MEM_Int | MEM_UInt | MEM_Real); return SQL_OK; } @@ -418,7 +422,7 @@ sqlVdbeMemRelease(Mem * p) * return the closest available 64-bit signed integer. */ static int -doubleToInt64(double r, int64_t *i) +doubleToInt64(double r, int64_t *i, bool *is_neg) { /* * Many compilers we encounter do not define constants for the @@ -429,7 +433,7 @@ doubleToInt64(double r, int64_t *i) */ static const int64_t maxInt = LARGEST_INT64; static const int64_t minInt = SMALLEST_INT64; - + *is_neg = r < 0.f; if (r <= (double)minInt) { *i = minInt; return -1; @@ -454,20 +458,24 @@ doubleToInt64(double r, int64_t *i) * If pMem represents a string value, its encoding might be changed. */ int -sqlVdbeIntValue(Mem * pMem, int64_t *i) +sqlVdbeIntValue(Mem * pMem, int64_t *i, bool *is_neg) { int flags; assert(EIGHT_BYTE_ALIGNMENT(pMem)); flags = pMem->flags; if (flags & MEM_Int) { *i = pMem->u.i; + *is_neg = true; + return 0; + } else if (flags & MEM_UInt) { + *i = pMem->u.u; + *is_neg = false; return 0; } else if (flags & MEM_Real) { - return doubleToInt64(pMem->u.r, i); + return doubleToInt64(pMem->u.r, i, is_neg); } else if (flags & (MEM_Str)) { assert(pMem->z || pMem->n == 0); - bool is_neg; - if (sql_atoi64(pMem->z, (int64_t *)i, &is_neg, pMem->n) == 0) + if (sql_atoi64(pMem->z, (int64_t *)i, is_neg, pMem->n) == 0) return 0; } return -1; @@ -489,7 +497,11 @@ sqlVdbeRealValue(Mem * pMem, double *v) } else if (pMem->flags & MEM_Int) { *v = (double)pMem->u.i; return 0; - } else if (pMem->flags & MEM_Str) { + } else if (pMem->flags & MEM_UInt) { + *v = (double)pMem->u.u; + return 0; + } + else if (pMem->flags & MEM_Str) { if (sqlAtoF(pMem->z, v, pMem->n)) return 0; } @@ -518,10 +530,9 @@ mem_apply_integer_type(Mem *pMem) assert(pMem->flags & MEM_Real); assert(EIGHT_BYTE_ALIGNMENT(pMem)); - if ((rc = doubleToInt64(pMem->u.r, (int64_t *) &ix)) == 0) { - pMem->u.i = ix; - MemSetTypeFlag(pMem, MEM_Int); - } + bool is_neg; + if ((rc = doubleToInt64(pMem->u.r, (int64_t *) &ix, &is_neg)) == 0) + mem_set_int(pMem, ix, is_neg); return rc; } @@ -534,15 +545,14 @@ sqlVdbeMemIntegerify(Mem * pMem, bool is_forced) assert(EIGHT_BYTE_ALIGNMENT(pMem)); int64_t i; - if (sqlVdbeIntValue(pMem, &i) == 0) { - pMem->u.i = i; - MemSetTypeFlag(pMem, MEM_Int); + bool is_neg; + if (sqlVdbeIntValue(pMem, &i, &is_neg) == 0) { + mem_set_int(pMem, i, is_neg); return 0; } else if ((pMem->flags & MEM_Real) != 0 && is_forced) { if (pMem->u.r >= INT64_MAX || pMem->u.r < INT64_MIN) return -1; - pMem->u.i = (int64_t) pMem->u.r; - MemSetTypeFlag(pMem, MEM_Int); + mem_set_int(pMem, pMem->u.r, pMem->u.r <= -1); return 0; } @@ -551,9 +561,7 @@ sqlVdbeMemIntegerify(Mem * pMem, bool is_forced) return -1; if (d >= INT64_MAX || d < INT64_MIN) return -1; - - pMem->u.i = (int64_t) d; - MemSetTypeFlag(pMem, MEM_Int); + mem_set_int(pMem, d, d <= -1); return 0; } @@ -585,12 +593,13 @@ sqlVdbeMemRealify(Mem * pMem) int sqlVdbeMemNumerify(Mem * pMem) { - if ((pMem->flags & (MEM_Int | MEM_Real | MEM_Null)) == 0) { + if ((pMem->flags & (MEM_Int | MEM_UInt | MEM_Real | MEM_Null)) == 0) { assert((pMem->flags & (MEM_Blob | MEM_Str)) != 0); bool is_neg; if (0 == sql_atoi64(pMem->z, (int64_t *)&pMem->u.i, &is_neg, pMem->n)) { - MemSetTypeFlag(pMem, MEM_Int); + int flag = is_neg ? MEM_Int : MEM_UInt; + MemSetTypeFlag(pMem, flag); } else { double v; if (sqlVdbeRealValue(pMem, &v)) @@ -600,7 +609,7 @@ sqlVdbeMemNumerify(Mem * pMem) mem_apply_integer_type(pMem); } } - assert((pMem->flags & (MEM_Int | MEM_Real | MEM_Null)) != 0); + assert((pMem->flags & (MEM_Int | MEM_UInt | MEM_Real | MEM_Null)) != 0); pMem->flags &= ~(MEM_Str | MEM_Blob | MEM_Zero); return SQL_OK; } @@ -671,6 +680,10 @@ sqlVdbeMemCast(Mem * pMem, enum field_type type) 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; @@ -688,15 +701,16 @@ sqlVdbeMemCast(Mem * pMem, enum field_type type) case FIELD_TYPE_INTEGER: if ((pMem->flags & MEM_Blob) != 0) { bool is_neg; - if (sql_atoi64(pMem->z, (int64_t *) &pMem->u.i, &is_neg, + int64_t val; + if (sql_atoi64(pMem->z, (int64_t *) &val, &is_neg, pMem->n) != 0) return -1; - MemSetTypeFlag(pMem, MEM_Int); + mem_set_int(pMem, val, is_neg); return 0; } if ((pMem->flags & MEM_Bool) != 0) { - pMem->u.i = pMem->u.b; - MemSetTypeFlag(pMem, MEM_Int); + pMem->u.u = pMem->u.b; + MemSetTypeFlag(pMem, MEM_UInt); return 0; } return sqlVdbeMemIntegerify(pMem, true); @@ -714,7 +728,8 @@ sqlVdbeMemCast(Mem * pMem, enum field_type type) 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_Real | MEM_Blob | MEM_Zero); + pMem->flags &= + ~(MEM_Int | MEM_UInt | MEM_Real | MEM_Blob | MEM_Zero); return SQL_OK; } } @@ -777,42 +792,29 @@ sqlVdbeMemSetZeroBlob(Mem * pMem, int n) pMem->z = 0; } -/* - * The pMem is known to contain content that needs to be destroyed prior - * to a value change. So invoke the destructor, then set the value to - * a 64-bit integer. - */ -static SQL_NOINLINE void -vdbeReleaseAndSetInt64(Mem * pMem, i64 val) +void +mem_set_bool(struct Mem *mem, bool value) { - sqlVdbeMemSetNull(pMem); - pMem->u.i = val; - pMem->flags = MEM_Int; + sqlVdbeMemSetNull(mem); + mem->u.b = value; + mem->flags = MEM_Bool; } -/* - * Delete any previous value and set the value stored in *pMem to val, - * manifest type INTEGER. - */ void -sqlVdbeMemSetInt64(Mem * pMem, i64 val) +mem_set_int(struct Mem *mem, int64_t value, bool is_neg) { - if (VdbeMemDynamic(pMem)) { - vdbeReleaseAndSetInt64(pMem, val); + if (VdbeMemDynamic(mem)) + sqlVdbeMemSetNull(mem); + if (is_neg) { + assert(value < 0); + mem->u.i = value; + MemSetTypeFlag(mem, MEM_Int); } else { - pMem->u.i = val; - pMem->flags = MEM_Int; + mem->u.u = value; + MemSetTypeFlag(mem, MEM_UInt); } } -void -mem_set_bool(struct Mem *mem, bool value) -{ - sqlVdbeMemSetNull(mem); - mem->u.b = value; - mem->flags = MEM_Bool; -} - /* * Delete any previous value and set the value stored in *pMem to val, * manifest type REAL. @@ -1377,8 +1379,8 @@ valueFromExpr(sql * db, /* The database connection */ if (pVal == 0) goto no_mem; if (ExprHasProperty(pExpr, EP_IntValue)) { - sqlVdbeMemSetInt64(pVal, - (i64) pExpr->u.iValue * negInt); + mem_set_int(pVal, (i64) pExpr->u.iValue * negInt, + pExpr->u.iValue * negInt < 0); } else { zVal = sqlMPrintf(db, "%s%s", zNeg, pExpr->u.zToken); @@ -1797,16 +1799,19 @@ mpstream_encode_vdbe_mem(struct mpstream *stream, struct Mem *var) * pass to INT iterator. */ i = var->u.r; - if (i == var->u.r) + if (i == var->u.r && i < 0) goto encode_int; + if (i == var->u.r && i >= 0) + goto encode_uint; mpstream_encode_double(stream, var->u.r); } else if (var->flags & MEM_Int) { i = var->u.i; encode_int: - if (var->u.i >= 0) - mpstream_encode_uint(stream, i); - else - mpstream_encode_int(stream, i); + mpstream_encode_int(stream, i); + } else if (var->flags & MEM_UInt) { + i = var->u.u; +encode_uint: + mpstream_encode_uint(stream, i); } else if (var->flags & MEM_Str) { mpstream_encode_strn(stream, var->z, var->n); } else if (var->flags & MEM_Bool) { diff --git a/test/sql-tap/position.test.lua b/test/sql-tap/position.test.lua index 8c46d7b9e..40b8a943b 100755 --- a/test/sql-tap/position.test.lua +++ b/test/sql-tap/position.test.lua @@ -228,7 +228,7 @@ test:do_test( return test:catchsql "SELECT position(34, 12345);" end, { -- - 1, "Inconsistent types: expected TEXT or BLOB got INTEGER" + 1, "Inconsistent types: expected TEXT or BLOB got UNSIGNED" -- }) diff --git a/test/sql/types.result b/test/sql/types.result index a53d6f7ce..4670fd38a 100644 --- a/test/sql/types.result +++ b/test/sql/types.result @@ -152,7 +152,7 @@ sp:drop() -- box.execute("SELECT 'abc' || 1;") --- -- error: 'Inconsistent types: expected TEXT or BLOB got INTEGER' +- error: 'Inconsistent types: expected TEXT or BLOB got UNSIGNED' ... box.execute("SELECT 'abc' || 1.123;") --- @@ -160,7 +160,7 @@ box.execute("SELECT 'abc' || 1.123;") ... box.execute("SELECT 1 || 'abc';") --- -- error: 'Inconsistent types: expected TEXT or BLOB got INTEGER' +- error: 'Inconsistent types: expected TEXT or BLOB got UNSIGNED' ... box.execute("SELECT 1.123 || 'abc';") --- @@ -168,7 +168,7 @@ box.execute("SELECT 1.123 || 'abc';") ... box.execute("SELECt 'a' || 'b' || 1;") --- -- error: 'Inconsistent types: expected TEXT or BLOB got INTEGER' +- error: 'Inconsistent types: expected TEXT or BLOB got UNSIGNED' ... -- What is more, they must be of the same type. -- @@ -230,11 +230,11 @@ box.execute("INSERT INTO t1 VALUES (1);") ... box.execute("SELECT * FROM t1 WHERE s LIKE 'int';") --- -- error: 'Inconsistent types: expected TEXT got INTEGER' +- error: 'Inconsistent types: expected TEXT got UNSIGNED' ... box.execute("SELECT * FROM t1 WHERE 'int' LIKE 4;") --- -- error: 'Inconsistent types: expected TEXT got INTEGER' +- error: 'Inconsistent types: expected TEXT got UNSIGNED' ... box.execute("SELECT NULL LIKE s FROM t1;") --- @@ -355,7 +355,7 @@ box.execute("SELECT unknown = true;") ... box.execute("SELECT 1 = true;") --- -- error: 'Type mismatch: can not convert INTEGER to boolean' +- error: 'Type mismatch: can not convert UNSIGNED to boolean' ... box.execute("SELECT 'abc' = true;") --- -- 2.15.1