diff --git a/src/box/execute.c b/src/box/execute.c index a9ca2e67a5a8..c03f9cd300a9 100644 --- a/src/box/execute.c +++ b/src/box/execute.c @@ -139,17 +139,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 4d90a53dedf4..21ea6a299aab 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 761a3abae5c2..2b06b07ed12e 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) { @@ -1017,6 +1020,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; @@ -1419,7 +1423,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, @@ -1450,7 +1455,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) @@ -1561,7 +1567,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"); @@ -1571,7 +1577,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 181f71deb3fe..ee5d85ec988f 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -260,13 +260,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) == 0) 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; @@ -309,12 +308,13 @@ 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, record->u.r, + record->u.r <= -1); return 0; } return sqlVdbeMemIntegerify(record, false); @@ -323,7 +323,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: @@ -332,11 +332,10 @@ mem_apply_type(struct Mem *record, enum field_type type) * an integer or real representation (BLOB and * NULL do not get converted). */ - if ((record->flags & MEM_Str) == 0) { - if ((record->flags & (MEM_Real | MEM_Int))) - sqlVdbeMemStringify(record, 1); - } - record->flags &= ~(MEM_Real | MEM_Int); + if ((record->flags & MEM_Str) == 0 && + (record->flags & (MEM_Real | MEM_Int | MEM_UInt)) != 0) + sqlVdbeMemStringify(record, 1); + record->flags &= ~(MEM_Real | MEM_Int | MEM_UInt); return 0; case FIELD_TYPE_SCALAR: return 0; @@ -365,13 +364,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) == 0) - return MEM_Int; + return is_neg ? MEM_Int : MEM_UInt; return MEM_Real; } @@ -384,9 +383,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)) != 0) + return pMem->flags & (MEM_Int | MEM_UInt | MEM_Real); if (pMem->flags & (MEM_Str|MEM_Blob)) { return computeNumericType(pMem); } @@ -490,6 +488,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) { @@ -588,6 +588,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: @@ -897,8 +899,7 @@ 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); + mem_set_u64(pIn1, pOp - aOp); REGISTER_TRACE(p, pOp->p1, pIn1); /* Most jump operations do a goto to this spot in order to update @@ -916,8 +917,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; } @@ -936,11 +937,10 @@ case OP_Return: { /* in1 */ case OP_InitCoroutine: { /* jump */ assert(pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor)); assert(pOp->p2>=0 && pOp->p2nOp); - assert(pOp->p3>=0 && pOp->p3nOp); + assert(pOp->p3>0 && pOp->p3nOp); pOut = &aMem[pOp->p1]; assert(!VdbeMemDynamic(pOut)); - pOut->u.i = pOp->p3 - 1; - pOut->flags = MEM_Int; + mem_set_u64(pOut, pOp->p3 - 1); if (pOp->p2) goto jump_to_p2; break; } @@ -956,9 +956,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]; @@ -980,12 +980,10 @@ case OP_EndCoroutine: { /* in1 */ * See also: InitCoroutine */ 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); + int pcDest = (int)pIn1->u.u; + mem_set_u64(pIn1, pOp - aOp); REGISTER_TRACE(p, pOp->p1, pIn1); pOp = &aOp[pcDest]; break; @@ -1064,7 +1062,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; + assert((pOut->flags & MEM_Int) != 0); + } else { + mem_set_u64(pOut, pOp->p1); + } break; } @@ -1089,7 +1092,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_i64(pOut, *pOp->p4.pI64); break; } @@ -1173,8 +1176,7 @@ case OP_NextAutoincValue: { goto abort_due_to_error; pOut = out2Prerelease(p, pOp); - pOut->flags = MEM_Int; - pOut->u.i = value; + mem_set_i64(pOut, value); break; } @@ -1370,9 +1372,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) != 0); break; } @@ -1582,7 +1584,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; @@ -1605,8 +1608,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ break; } } - pOut->u.i = iB; - MemSetTypeFlag(pOut, MEM_Int); + mem_set_i64(pOut, iB); } else { bIntint = 0; if (sqlVdbeRealValue(pIn1, &rA) != 0) { @@ -1831,12 +1833,13 @@ 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"); 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"); goto abort_due_to_error; @@ -1870,8 +1873,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_i64(pOut, iA); break; } @@ -1879,15 +1881,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; } @@ -1900,10 +1901,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) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_text(pIn1), "integer"); @@ -1913,7 +1913,6 @@ case OP_MustBeInt: { /* jump, in1 */ } } } - MemSetTypeFlag(pIn1, MEM_Int); break; } @@ -1928,7 +1927,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; @@ -2117,12 +2116,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_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_Str) == MEM_Str) { if (mem_apply_numeric_type(pIn3) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, @@ -2136,14 +2135,16 @@ 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); @@ -2151,7 +2152,8 @@ 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); @@ -2432,13 +2434,13 @@ 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"); goto abort_due_to_error; } - pOut->flags = MEM_Int; - pOut->u.i = ~i; + mem_set_i64(pOut, ~i); } break; } @@ -2614,9 +2616,13 @@ case OP_Column: { default_val_mem != NULL) { sqlVdbeMemShallowCopy(pDest, default_val_mem, MEM_Static); } - if ((pDest->flags & MEM_Int) != 0) { - if (field_type == FIELD_TYPE_NUMBER) - sqlVdbeMemSetDouble(pDest, pDest->u.i); + if ((pDest->flags & (MEM_Int | MEM_UInt)) != 0) { + if (field_type == FIELD_TYPE_NUMBER) { + if ((pDest->flags & MEM_Int) != 0) + sqlVdbeMemSetDouble(pDest, pDest->u.i); + else + sqlVdbeMemSetDouble(pDest, pDest->u.u); + } } op_column_out: REGISTER_TRACE(p, pOp->p3, pDest); @@ -2799,7 +2805,7 @@ case OP_Count: { /* out2 */ nEntry = tarantoolsqlEphemeralCount(pCrsr); } pOut = out2Prerelease(p, pOp); - pOut->u.i = nEntry; + mem_set_u64(pOut, nEntry); break; } @@ -2910,7 +2916,8 @@ case OP_Savepoint: { case OP_CheckViewReferences: { assert(pOp->p1 > 0); pIn1 = &aMem[pOp->p1]; - uint32_t space_id = pIn1->u.i; + uint64_t space_id = pIn1->u.u; + assert(space_id <= INT32_MAX); struct space *space = space_by_id(space_id); assert(space != NULL); if (space->def->view_ref_count > 0) { @@ -3299,6 +3306,7 @@ case OP_SeekGT: { /* jump, in3 */ #endif iKey = 0; reg_ipk = pOp->p5; + bool is_neg = false; if (reg_ipk > 0) { @@ -3307,12 +3315,15 @@ 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_Str) != 0) 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; @@ -3320,6 +3331,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"); @@ -3330,7 +3342,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 @@ -3386,10 +3398,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); @@ -3599,7 +3609,8 @@ 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++; + mem_set_u64(pOut, seq_val); break; } @@ -3612,10 +3623,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; + uint64_t id; + tarantoolSqlNextSeqId(&id); + id++; + mem_set_u64(pOut, id); break; } @@ -3645,8 +3656,7 @@ case OP_NextIdEphemeral: { goto abort_due_to_error; } pOut = &aMem[pOp->p2]; - pOut->u.i = rowid; - pOut->flags = MEM_Int; + mem_set_u64(pOut, rowid); break; } @@ -3678,12 +3688,10 @@ 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_Int); } break; } @@ -4851,10 +4859,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; @@ -4879,14 +4894,13 @@ case OP_IfPos: { /* jump, in1 */ * Otherwise, r[P2] is set to the sum of r[P1] and r[P3]. */ case OP_OffsetLimit: { /* in1, out2, in3 */ - i64 x; pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; pOut = out2Prerelease(p, pOp); - assert(pIn1->flags & MEM_Int); - assert(pIn3->flags & MEM_Int); - x = pIn1->u.i; - if (x<=0 || sqlAddInt64(&x, pIn3->u.i>0?pIn3->u.i:0)) { + assert((pIn1->flags & MEM_UInt) != 0); + assert((pIn3->flags & MEM_UInt) != 0); + uint64_t x = pIn1->u.u; + if (x == 0 || sqlAddInt64((i64 *) &x, pIn3->u.u)) { /* 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 @@ -4895,9 +4909,9 @@ case OP_OffsetLimit: { /* in1, out2, in3 */ * it would take nearly 300 years to actually reach the limit. So * looping forever is a reasonable approximation. */ - pOut->u.i = -1; + mem_set_i64(pOut, -1); } else { - pOut->u.i = x; + mem_set_u64(pOut, x); } break; } @@ -4912,10 +4926,9 @@ case OP_OffsetLimit: { /* in1, out2, in3 */ */ case OP_IfNotZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - assert(pIn1->flags&MEM_Int); - VdbeBranchTaken(pIn1->u.i<0, 2); - if (pIn1->u.i) { - if (pIn1->u.i>0) pIn1->u.i--; + assert((pIn1->flags & MEM_UInt) != 0); + if (pIn1->u.u > 0) { + pIn1->u.u--; goto jump_to_p2; } break; @@ -4929,10 +4942,10 @@ case OP_IfNotZero: { /* jump, in1 */ */ case OP_DecrJumpZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - assert(pIn1->flags&MEM_Int); - if (pIn1->u.i>SMALLEST_INT64) pIn1->u.i--; - VdbeBranchTaken(pIn1->u.i==0, 2); - if (pIn1->u.i==0) goto jump_to_p2; + assert((pIn1->flags & MEM_UInt) != 0); + if (pIn1->u.u > 0) + pIn1->u.u--; + if (pIn1->u.u == 0) goto jump_to_p2; break; } @@ -5154,9 +5167,9 @@ case OP_IncMaxid: { assert(pOp->p1 > 0); pOut = &aMem[pOp->p1]; - if (tarantoolsqlIncrementMaxid((uint64_t*) &pOut->u.i) != 0) + if (tarantoolsqlIncrementMaxid(&pOut->u.u) != 0) goto abort_due_to_error; - pOut->flags = MEM_Int; + pOut->flags = MEM_UInt; break; } diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h index 6bfeecc85f58..b83926a669ef 100644 --- a/src/box/sql/vdbeInt.h +++ b/src/box/sql/vdbeInt.h @@ -164,6 +164,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 */ @@ -211,7 +212,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 an 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 */ @@ -229,6 +230,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 */ /** * In contrast to Mem_TypeMask, this one allows to get @@ -236,9 +238,14 @@ struct Mem { * auxiliary flags. */ enum { - MEM_PURE_TYPE_MASK = 0x3f + MEM_PURE_TYPE_MASK = 0x7f }; +static_assert(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. @@ -451,7 +458,6 @@ void sqlVdbeMemShallowCopy(Mem *, const Mem *, int); void sqlVdbeMemMove(Mem *, Mem *); int sqlVdbeMemNulTerminate(Mem *); int sqlVdbeMemSetStr(Mem *, const char *, int, u8, void (*)(void *)); -void sqlVdbeMemSetInt64(Mem *, i64); void mem_set_bool(struct Mem *mem, bool value); @@ -464,13 +470,32 @@ mem_set_bool(struct Mem *mem, bool value); void mem_set_ptr(struct Mem *mem, void *ptr); +/** + * Set integer value. Depending on its sign MEM_Int (in case + * of negative value) or MEM_UInt flag is set. + */ +void +mem_set_i64(struct Mem *mem, int64_t value); + +/** Set unsigned value and MEM_UInt flag. */ +void +mem_set_u64(struct Mem *mem, uint64_t value); + +/** + * Set integer value. According to is_neg flag value is considered + * to be signed or unsigned. + */ +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 f470ac6b1f8a..705e869bc104 100644 --- a/src/box/sql/vdbeapi.c +++ b/src/box/sql/vdbeapi.c @@ -166,7 +166,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; } @@ -174,7 +175,8 @@ sql_int64 sql_value_int64(sql_value * pVal) { int64_t i = 0; - sqlVdbeIntValue((Mem *) pVal, &i); + bool unused; + sqlVdbeIntValue((Mem *) pVal, &i, &unused); return i; } @@ -199,6 +201,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; @@ -319,7 +322,7 @@ sql_result_double(sql_context * pCtx, double rVal) void sql_result_int(sql_context * pCtx, int iVal) { - sqlVdbeMemSetInt64(pCtx->pOut, (i64) iVal); + mem_set_i64(pCtx->pOut, iVal); } void @@ -331,7 +334,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_i64(pCtx->pOut, iVal); } void @@ -995,7 +998,7 @@ sql_bind_int64(sql_stmt * pStmt, int i, sql_int64 iValue) if (vdbeUnbind(p, i) != 0) return -1; int rc = sql_bind_type(p, i, "INTEGER"); - sqlVdbeMemSetInt64(&p->aVar[i - 1], iValue); + mem_set_i64(&p->aVar[i - 1], iValue); return rc; } diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c index baeeb46242a7..9159b39fd00c 100644 --- a/src/box/sql/vdbeaux.c +++ b/src/box/sql/vdbeaux.c @@ -1183,6 +1183,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) { @@ -1427,8 +1429,9 @@ sqlVdbeList(Vdbe * p) pOp = &apSub[j]->aOp[i]; } if (p->explain == 1) { - pMem->flags = MEM_Int; - pMem->u.i = i; /* Program counter */ + assert(i >= 0); + mem_set_u64(pMem, i); + pMem++; pMem->flags = MEM_Static | MEM_Str | MEM_Term; @@ -1460,16 +1463,13 @@ sqlVdbeList(Vdbe * p) } } - pMem->flags = MEM_Int; - pMem->u.i = pOp->p1; /* P1 */ + mem_set_i64(pMem, pOp->p1); pMem++; - pMem->flags = MEM_Int; - pMem->u.i = pOp->p2; /* P2 */ + mem_set_i64(pMem, pOp->p2); pMem++; - pMem->flags = MEM_Int; - pMem->u.i = pOp->p3; /* P3 */ + mem_set_i64(pMem, pOp->p3); pMem++; if (sqlVdbeMemClearAndResize(pMem, 256)) { @@ -2882,43 +2882,50 @@ sqlBlobCompare(const Mem * pB1, const Mem * pB2) return n1 - n2; } -/* - * Do a comparison between a 64-bit signed integer and a 64-bit floating-point - * number. Return negative, zero, or positive if the first (i64) is less than, - * equal to, or greater than the second (double). +/** + * Do a comparison between a 64-bit unsigned/signed integer and a + * 64-bit floating-point number. Return negative, zero, or + * positive if the first (integer) is less than, equal to, or + * greater than the second (double). */ static int -sqlIntFloatCompare(i64 i, double r) +compare_uint_float(uint64_t u, double r) { - if (sizeof(LONGDOUBLE_TYPE) > 8) { - LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE) i; - if (x < r) - return -1; - if (x > r) - return +1; - return 0; - } else { - i64 y; - double s; - if (r < -9223372036854775808.0) - return +1; - if (r > 9223372036854775807.0) - return -1; - y = (i64) r; - if (i < y) - return -1; - if (i > y) { - if (y == SMALLEST_INT64 && r > 0.0) - return -1; - return +1; - } - s = (double)i; - if (s < r) - return -1; - if (s > r) - return +1; - return 0; - } + if (r > (double) UINT64_MAX) + return -1; + if (r < 0.0) + return +1; + uint64_t y = (uint64_t) r; + if (u < y) + return -1; + if (u > y) + return +1; + double s = (double) u; + if (s < r) + return -1; + if (s > r) + return +1; + return 0; +} + +static int +compare_int_float(int64_t i, double r) +{ + if (r < (double) INT64_MIN) + return +1; + if (r >= 0.0) + return -1; + int64_t y = (int64_t) r; + if (i < y) + return -1; + if (i > y) + return +1; + double s = (double) i; + if (s < r) + return -1; + if (s > r) + return +1; + return 0; } /* @@ -2960,7 +2967,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)) != 0) { if ((f1 & f2 & MEM_Int) != 0) { if (pMem1->u.i < pMem2->u.i) return -1; @@ -2968,6 +2975,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; @@ -2977,16 +2991,29 @@ sqlMemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pColl) } if ((f1 & MEM_Int) != 0) { if ((f2 & MEM_Real) != 0) { - return sqlIntFloatCompare(pMem1->u.i, - pMem2->u.r); + return compare_int_float(pMem1->u.i, + pMem2->u.r); + } else { + return -1; + } + } + if ((f1 & MEM_UInt) != 0) { + if ((f2 & MEM_Real) != 0) { + return compare_uint_float(pMem1->u.u, + 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); + return -compare_int_float(pMem2->u.i, + pMem1->u.r); + } else if ((f2 & MEM_UInt) != 0) { + return -compare_uint_float(pMem2->u.u, + pMem1->u.r); } else { return -1; } @@ -3146,26 +3173,33 @@ sqlVdbeCompareMsgpack(const char **key1, break; } case MP_UINT:{ - uint64_t v = mp_decode_uint(&aKey1); - if (v > INT64_MAX) { - mem1.u.r = (double)v; - goto do_float; + mem1.u.u = mp_decode_uint(&aKey1); + if ((pKey2->flags & MEM_Int) != 0) { + rc = +1; + } else if ((pKey2->flags & MEM_UInt) != 0) { + if (mem1.u.u < pKey2->u.u) + rc = -1; + else if (mem1.u.u > pKey2->u.u) + rc = +1; + } else if ((pKey2->flags & MEM_Real) != 0) { + rc = compare_uint_float(mem1.u.u, pKey2->u.r); + } else { + rc = (pKey2->flags & MEM_Null) ? +1 : -1; } - mem1.u.i = v; - goto do_int; + break; } case MP_INT:{ mem1.u.i = mp_decode_int(&aKey1); - do_int: - if (pKey2->flags & MEM_Int) { + if ((pKey2->flags & MEM_UInt) != 0) { + rc = -1; + } else if ((pKey2->flags & MEM_Int) != 0) { if (mem1.u.i < pKey2->u.i) { rc = -1; } else if (mem1.u.i > pKey2->u.i) { rc = +1; } } else if (pKey2->flags & MEM_Real) { - rc = sqlIntFloatCompare(mem1.u.i, - pKey2->u.r); + rc = compare_int_float(mem1.u.i, pKey2->u.r); } else { rc = (pKey2->flags & MEM_Null) ? +1 : -1; } @@ -3178,9 +3212,10 @@ sqlVdbeCompareMsgpack(const char **key1, case MP_DOUBLE:{ mem1.u.r = mp_decode_double(&aKey1); do_float: - if (pKey2->flags & MEM_Int) { - rc = -sqlIntFloatCompare(pKey2->u.i, - mem1.u.r); + if ((pKey2->flags & MEM_Int) != 0) { + rc = -compare_int_float(pKey2->u.i, mem1.u.r); + } else if (pKey2->flags & MEM_UInt) { + rc = -compare_uint_float(pKey2->u.u, mem1.u.r); } else if (pKey2->flags & MEM_Real) { if (mem1.u.r < pKey2->u.r) { rc = -1; @@ -3304,8 +3339,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 f8654c03429d..cf738dcc7c20 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 @@ -284,7 +286,7 @@ sqlVdbeMemStringify(Mem * pMem, u8 bForce) return 0; assert(!(fg & MEM_Zero)); - assert(fg & (MEM_Int | MEM_Real | MEM_Bool)); + assert((fg & (MEM_Int | MEM_UInt | MEM_Real | MEM_Bool)) != 0); assert(EIGHT_BYTE_ALIGNMENT(pMem)); if (sqlVdbeMemClearAndResize(pMem, nByte)) { @@ -292,6 +294,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 { @@ -301,7 +305,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 0; } @@ -423,7 +427,6 @@ doubleToInt64(double r, int64_t *i) */ static const int64_t maxInt = LARGEST_INT64; static const int64_t minInt = SMALLEST_INT64; - if (r <= (double)minInt) { *i = minInt; return -1; @@ -448,20 +451,25 @@ 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) { + *is_neg = pMem->u.r < 0; return doubleToInt64(pMem->u.r, i); } else if (flags & (MEM_Str)) { assert(pMem->z || pMem->n == 0); - bool is_neg; - if (sql_atoi64(pMem->z, i, &is_neg, pMem->n) == 0) + if (sql_atoi64(pMem->z, i, is_neg, pMem->n) == 0) return 0; } return -1; @@ -483,6 +491,9 @@ sqlVdbeRealValue(Mem * pMem, double *v) } else if (pMem->flags & MEM_Int) { *v = (double)pMem->u.i; return 0; + } else if ((pMem->flags & MEM_UInt) != 0) { + *v = (double)pMem->u.u; + return 0; } else if (pMem->flags & MEM_Str) { if (sqlAtoF(pMem->z, v, pMem->n)) return 0; @@ -512,10 +523,8 @@ 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); - } + if ((rc = doubleToInt64(pMem->u.r, (int64_t *) &ix)) == 0) + mem_set_int(pMem, ix, pMem->u.r <= -1); return rc; } @@ -528,15 +537,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; } @@ -545,9 +553,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; } @@ -579,12 +585,12 @@ 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 (sql_atoi64(pMem->z, (int64_t *) &pMem->u.i, &is_neg, - pMem->n) == 0) { - MemSetTypeFlag(pMem, MEM_Int); + int64_t i; + if (sql_atoi64(pMem->z, &i, &is_neg, pMem->n) == 0) { + mem_set_int(pMem, i, is_neg); } else { double v; if (sqlVdbeRealValue(pMem, &v)) @@ -594,7 +600,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 0; } @@ -665,6 +671,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; @@ -682,15 +692,15 @@ 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, pMem->n) != 0) + int64_t val; + if (sql_atoi64(pMem->z, &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); @@ -708,7 +718,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 0; } } @@ -779,40 +790,46 @@ 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_i64(struct Mem *mem, int64_t value) { - if (VdbeMemDynamic(pMem)) { - vdbeReleaseAndSetInt64(pMem, val); - } else { - pMem->u.i = val; - pMem->flags = MEM_Int; - } + if (VdbeMemDynamic(mem)) + sqlVdbeMemSetNull(mem); + mem->u.i = value; + int flag = value < 0 ? MEM_Int : MEM_UInt; + MemSetTypeFlag(mem, flag); } void -mem_set_bool(struct Mem *mem, bool value) +mem_set_u64(struct Mem *mem, uint64_t value) { - sqlVdbeMemSetNull(mem); - mem->u.b = value; - mem->flags = MEM_Bool; + if (VdbeMemDynamic(mem)) + sqlVdbeMemSetNull(mem); + mem->u.u = value; + MemSetTypeFlag(mem, MEM_UInt); +} + +void +mem_set_int(struct Mem *mem, int64_t value, bool is_neg) +{ + if (VdbeMemDynamic(mem)) + sqlVdbeMemSetNull(mem); + if (is_neg) { + assert(value < 0); + mem->u.i = value; + MemSetTypeFlag(mem, MEM_Int); + } else { + mem->u.u = value; + MemSetTypeFlag(mem, MEM_UInt); + } } /* @@ -1378,8 +1395,7 @@ 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_i64(pVal, (i64) pExpr->u.iValue * negInt); } else { zVal = sqlMPrintf(db, "%s%s", zNeg, pExpr->u.zToken); @@ -1404,11 +1420,15 @@ valueFromExpr(sql * db, /* The database connection */ return rc; if (pVal->flags & MEM_Real) { pVal->u.r = -pVal->u.r; - } else if (pVal->u.i == SMALLEST_INT64) { - pVal->u.r = -(double)SMALLEST_INT64; - MemSetTypeFlag(pVal, MEM_Real); - } else { - pVal->u.i = -pVal->u.i; + } else if ((pVal->flags & MEM_Int) != 0) { + mem_set_u64(pVal, (uint64_t)(-pVal->u.i)); + } else if ((pVal->flags & MEM_UInt) != 0) { + if (pVal->u.u > (uint64_t) INT64_MAX + 1) { + pVal->u.r = -(double) pVal->u.u; + MemSetTypeFlag(pVal, MEM_Real); + } else { + mem_set_i64(pVal, (int64_t)(-pVal->u.u)); + } } sql_value_apply_type(pVal, type); } @@ -1797,16 +1817,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 8c46d7b9eac7..40b8a943bf34 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/integer-overflow.result b/test/sql/integer-overflow.result index a4944d1a042a..13914ad7b130 100644 --- a/test/sql/integer-overflow.result +++ b/test/sql/integer-overflow.result @@ -52,7 +52,7 @@ box.execute('SELECT CAST(\'9223372036854775808\' AS INTEGER);') - name: CAST('9223372036854775808' AS INTEGER) type: integer rows: - - [-9223372036854775808] + - [9223372036854775808] ... -- Due to inexact represantation of large integers in terms of -- floating point numbers, numerics with value < INT64_MAX diff --git a/test/sql/types.result b/test/sql/types.result index cdfb1e783f50..005fc5d660a6 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;") ---