[tarantool-patches] [PATCH v2 12/15] sql: support -2^63 .. 2^64-1 integer type
Stanislav Zudin
szudin at tarantool.org
Mon Apr 1 23:44:50 MSK 2019
Adds functions setting and retrieving unsigned value.
Adds a new member to struct sql_bind and struct Mem
to keep an unsigned value
atoi functions return 0 on success and -1 on error
and return the signed/unsigned property by pointer.
The cast operations take the unsigned into account.
Fixes the affected tests.
The sql function avg() uses uint64.
Part of #3810
---
src/box/execute.c | 9 ++--
src/box/lua/lua_sql.c | 2 +-
src/box/lua/sql.c | 2 +-
src/box/sql/expr.c | 30 +++++++------
src/box/sql/func.c | 38 +++++++++++------
src/box/sql/sqlInt.h | 63 +++++++++++----------------
src/box/sql/util.c | 72 ++++++++++++++-----------------
src/box/sql/vdbe.c | 30 +++++++------
src/box/sql/vdbeInt.h | 4 +-
src/box/sql/vdbeapi.c | 30 ++++++++++---
src/box/sql/vdbemem.c | 73 +++++++++++++++++++++-----------
test/sql-tap/func.test.lua | 2 +-
test/sql/integer-overflow.result | 4 +-
13 files changed, 201 insertions(+), 158 deletions(-)
diff --git a/src/box/execute.c b/src/box/execute.c
index 210b9a228..813208783 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -77,6 +77,7 @@ struct sql_bind {
union {
double d;
int64_t i64;
+ uint64_t u64;
/** For string or blob. */
const char *s;
};
@@ -130,9 +131,9 @@ sql_bind_decode(struct sql_bind *bind, int i, const char **packet)
switch (mp_typeof(**packet)) {
case MP_UINT: {
uint64_t n = mp_decode_uint(packet);
- bind->i64 = (int64_t) n;
+ bind->u64 = n;
bind->type = (n > INT64_MAX) ? SQL_UNSIGNED : SQL_INTEGER;
- bind->bytes = sizeof(bind->i64);
+ bind->bytes = sizeof(bind->u64);
break;
}
case MP_INT:
@@ -256,7 +257,7 @@ sql_column_to_messagepack(struct sql_stmt *stmt, int i,
break;
}
case SQL_UNSIGNED: {
- int64_t n = sql_column_int64(stmt, i);
+ uint64_t n = sql_column_uint64(stmt, i);
size = mp_sizeof_uint(n);
char *pos = (char *) region_alloc(region, size);
if (pos == NULL)
@@ -394,7 +395,7 @@ sql_bind_column(struct sql_stmt *stmt, const struct sql_bind *p,
rc = sql_bind_int64(stmt, pos, p->i64);
break;
case SQL_UNSIGNED:
- rc = sql_bind_uint64(stmt, pos, p->i64);
+ rc = sql_bind_uint64(stmt, pos, p->u64);
break;
case SQL_FLOAT:
rc = sql_bind_double(stmt, pos, p->d);
diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c
index 57a3161c7..f544c078d 100644
--- a/src/box/lua/lua_sql.c
+++ b/src/box/lua/lua_sql.c
@@ -61,7 +61,7 @@ lua_sql_call(sql_context *pCtx, int nVal, sql_value **apVal) {
luaL_pushint64(L, sql_value_int64(param));
break;
case SQL_UNSIGNED:
- luaL_pushuint64(L, sql_value_int64(param));
+ luaL_pushuint64(L, sql_value_uint64(param));
break;
case SQL_FLOAT:
lua_pushnumber(L, sql_value_double(param));
diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c
index 76bb44fa1..e7872c716 100644
--- a/src/box/lua/sql.c
+++ b/src/box/lua/sql.c
@@ -35,7 +35,7 @@ lua_push_row(struct lua_State *L, struct sql_stmt *stmt)
luaL_pushint64(L, sql_column_int64(stmt, i));
break;
case SQL_UNSIGNED:
- luaL_pushuint64(L, sql_column_int64(stmt, i));
+ luaL_pushuint64(L, sql_column_uint64(stmt, i));
break;
case SQL_FLOAT:
lua_pushnumber(L, sql_column_double(stmt, i));
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index fcc673436..0c127e80a 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -1192,7 +1192,9 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
* variable number
*/
int64_t i;
- bool is_ok = 0 == sql_atoi64(&z[1], &i, n - 1);
+ bool is_unsigned = false;
+ bool is_ok = 0 == sql_atoi64(&z[1],
+ &i, &is_unsigned, n - 1);
x = (ynVar) i;
testcase(i == 0);
testcase(i == 1);
@@ -3335,9 +3337,9 @@ expr_code_int(struct Parse *parse, struct Expr *expr, bool is_neg,
int64_t value;
const char *z = expr->u.zToken;
assert(z != NULL);
- enum atoi_result c = sql_dec_or_hex_to_i64(z, is_neg, &value);
- switch(c) {
- case ATOI_OVERFLOW:
+ bool is_unsigned = false;
+ int c = sql_dec_or_hex_to_i64(z, is_neg, &value, &is_unsigned);
+ if (c < 0) {
if (sql_strnicmp(z, "0x", 2) == 0) {
sqlErrorMsg(parse,
"hex literal too big: %s%s",
@@ -3347,15 +3349,17 @@ expr_code_int(struct Parse *parse, struct Expr *expr, bool is_neg,
"oversized integer: %s%s",
is_neg ? "-" : "", z);
}
- break;
- case ATOI_UNSIGNED:
- sqlVdbeAddOp4Dup8(v, OP_Int64, 0, mem, 0,
- (u8 *)&value, P4_UINT64);
- break;
- case ATOI_SIGNED:
- sqlVdbeAddOp4Dup8(v, OP_Int64, 0, mem, 0,
- (u8 *)&value, P4_INT64);
- break;
+ } else {
+ if (is_unsigned)
+ /*
+ * value is in the range
+ * [INT64_MAX+1, UINT64_MAX]
+ */
+ sqlVdbeAddOp4Dup8(v, OP_Int64, 0, mem, 0,
+ (u8 *)&value, P4_UINT64);
+ else
+ sqlVdbeAddOp4Dup8(v, OP_Int64, 0, mem, 0,
+ (u8 *)&value, P4_INT64);
}
}
}
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 194dec252..f1b894f16 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -193,7 +193,7 @@ absFunc(sql_context * context, int argc, sql_value ** argv)
break;
}
case SQL_UNSIGNED: {
- i64 iVal = sql_value_int64(argv[0]);
+ u64 iVal = sql_value_uint64(argv[0]);
sql_result_int64(context, iVal);
break;
}
@@ -1434,29 +1434,31 @@ sumStep(sql_context * context, int argc, sql_value ** argv)
type = sql_value_numeric_type(argv[0]);
if (p && type != SQL_NULL) {
p->cnt++;
- i64 v = 0;
- bool is_signed = false;
+ enum arithmetic_result rc = ATHR_SIGNED;
if (type == SQL_INTEGER) {
- v = sql_value_int64(argv[0]);
+ i64 v = sql_value_int64(argv[0]);
p->rSum += v;
- is_signed = true;
+ if ((p->approx | p->overflow) == 0)
+ rc = sqlAddInt64(&p->iSum,
+ p->is_unsigned == 0,
+ v, true);
} else if (type == SQL_UNSIGNED) {
- v = sql_value_int64(argv[0]);
- p->rSum += (u64)v;
- is_signed = false;
+ u64 v = sql_value_uint64(argv[0]);
+ p->rSum += v;
+ if ((p->approx | p->overflow) == 0)
+ rc = sqlAddInt64(&p->iSum,
+ p->is_unsigned == 0,
+ v, false);
} else {
p->rSum += sql_value_double(argv[0]);
p->approx = 1;
return;
}
- /* proceed with the integer value */
+ /* process the result of integer addition */
if ((p->approx | p->overflow) == 0) {
- enum arithmetic_result r = sqlAddInt64(&p->iSum,
- p->is_unsigned == 0,
- v, is_signed);
- switch (r) {
+ switch (rc) {
case ATHR_SIGNED:
break;
case ATHR_UNSIGNED:
@@ -1494,7 +1496,15 @@ avgFinalize(sql_context * context)
SumCtx *p;
p = sql_aggregate_context(context, 0);
if (p && p->cnt > 0) {
- sql_result_double(context, p->rSum / (double)p->cnt);
+ if (p->overflow || p->approx) {
+ sql_result_double(context, p->rSum / (double)p->cnt);
+ } else if (p->is_unsigned) {
+ u64 s = (u64)p->iSum;
+ sql_result_double(context, (double)s / (double)p->cnt);
+ } else {
+ sql_result_double(context, (double)p->iSum / (double)p->cnt);
+ }
+
}
}
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index df051a32b..fd87366df 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -452,6 +452,9 @@ sql_column_subtype(struct sql_stmt *stmt, int i);
sql_int64
sql_value_int64(sql_value *);
+sql_uint64
+sql_value_uint64(sql_value *);
+
const unsigned char *
sql_value_text(sql_value *);
@@ -582,6 +585,9 @@ sql_column_int(sql_stmt *, int iCol);
sql_int64
sql_column_int64(sql_stmt *, int iCol);
+sql_uint64
+sql_column_uint64(sql_stmt *, int iCol);
+
const unsigned char *
sql_column_text(sql_stmt *,
int iCol);
@@ -4277,37 +4283,21 @@ enum field_type *
field_type_sequence_dup(struct Parse *parse, enum field_type *types,
uint32_t len);
-enum atoi_result {
- /** Successful transformation.
- * Fits in a 64-bit signed integer.
- */
- ATOI_SIGNED = 0,
- /** Integer is too large for a 64-bit
- * unsigned integer or is malformed
- */
- ATOI_OVERFLOW = 1,
- /** Successful transformation.
- * Fits in a 64-bit unsigned integer.
- */
- ATOI_UNSIGNED = 2
-};
-
-
/**
* Converts z to a 64-bit signed or unsigned integer.
* z must be decimal. This routine does *not* accept
* hexadecimal notation.
*
* If the z value is representable as a 64-bit twos-complement
- * integer, then write that value into *val and return ATOI_SIGNED.
+ * integer, then write that value into *val and return 0.
*
* If z is a number in the range
* [9223372036854775808, 18446744073709551615] function returns
- * ATOI_UNSIGNED and result must be treated as unsigned.
+ * 0 and is_unsigned = true, the result must be treated as unsigned.
*
- * If z is too big for a 64-bit integer and is not
- * 9223372036854775808 or if z contains any non-numeric text,
- * then return ATOI_OVERFLOW.
+ * If z is too big for a 64-bit unsigned integer
+ * or if z contains any non-numeric text,
+ * then return -1.
*
* length is the number of bytes in the string (bytes, not
* characters). The string is not necessarily zero-terminated.
@@ -4315,17 +4305,15 @@ enum atoi_result {
*
* @param z String being parsed.
* @param[out] val Output integer value.
+ * @param[out] is_unsigned is true is returned value is positive
+ * and its value is in the range [INT64_MAX+1, UINT64_MAX]
* @param length String length in bytes.
* @retval
- * ATOI_SIGNED Successful transformation.
- * Fits in a 64-bit signed integer
- * ATOI_OVERFLOW Integer too large for a 64-bit
- * unsigned integer or is malformed
- * ATOI_UNSIGNED Successful transformation.
- * Fits in a 64-bit signed integer
+ * 0 Successful transformation.
+ * -1 An error occurred.
*/
-enum atoi_result
-sql_atoi64(const char *z, int64_t *val, int length);
+int
+sql_atoi64(const char *z, int64_t *val, bool *is_unsigned, int length);
/**
* Transform a UTF-8 integer literal, in either decimal or
@@ -4335,16 +4323,15 @@ sql_atoi64(const char *z, int64_t *val, int length);
* @param z Literal being parsed.
* @param is_neg Sign of the number being converted
* @param[out] val Parsed value.
+ * @param[out] is_unsigned is true is returned value is positive
+ * and its value is in the range [INT64_MAX+1, UINT64_MAX]
* @retval
- * ATOI_SIGNED Successful transformation.
- * Fits in a 64-bit signed integer
- * ATOI_OVERFLOW Integer too large for a 64-bit
- * unsigned integer or is malformed
- * ATOI_UNSIGNED Successful transformation.
- * Fits in a 64-bit signed integer
- */
-enum atoi_result
-sql_dec_or_hex_to_i64(const char *z, bool is_neg, int64_t *val);
+ * 0 Successful transformation.
+ * -1 An error occurred.
+ */
+int
+sql_dec_or_hex_to_i64(const char *z, bool is_neg, int64_t *val,
+ bool *is_unsigned);
void sqlErrorWithMsg(sql *, int, const char *, ...);
void sqlError(sql *, int);
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index 62c43c555..ce3f1b5c3 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -596,8 +596,8 @@ sqlAtoF(const char *z, double *pResult, int length)
#define INT64_MIN_MOD 0x8000000000000000
#endif
-enum atoi_result
-sql_atoi64(const char *z, int64_t *val, int length)
+int
+sql_atoi64(const char *z, int64_t *val, bool *is_unsigned, int length)
{
const char* expected_end = z + length;
int neg = 0; /* assume positive */
@@ -607,7 +607,7 @@ sql_atoi64(const char *z, int64_t *val, int length)
z += incr;
if (z >= zEnd)
- return ATOI_OVERFLOW; /* invalid format */
+ return -1; /* invalid format */
if (*z == '-') {
neg = 1;
z += incr;
@@ -617,32 +617,30 @@ sql_atoi64(const char *z, int64_t *val, int length)
errno = 0;
u64 u = strtoull(z, &end, 10);
if (end < expected_end)
- return ATOI_OVERFLOW;
+ return -1;
if (errno != 0)
- return ATOI_OVERFLOW;
+ return -1;
- enum atoi_result rc;
if (neg) {
- rc = ATOI_SIGNED;
+ *is_unsigned = false;
if (u <= INT64_MAX)
*val = -u;
else if (u == INT64_MIN_MOD)
*val = (i64) u;
else
- rc = ATOI_OVERFLOW;
+ return -1;
} else {
*val = (i64) u;
- rc = (u <= INT64_MAX) ? ATOI_SIGNED
- : ATOI_UNSIGNED;
+ *is_unsigned = (u > INT64_MAX);
}
- return rc;
+ return 0;
}
-enum atoi_result
-sql_dec_or_hex_to_i64(const char *z, bool is_neg, int64_t *val)
+int
+sql_dec_or_hex_to_i64(const char *z, bool is_neg, int64_t *val,
+ bool *is_unsigned)
{
- enum atoi_result rc;
if (z[0] == '0' && (z[1] == 'x' || z[1] == 'X')) {
uint64_t u = 0;
int i, k;
@@ -653,38 +651,33 @@ sql_dec_or_hex_to_i64(const char *z, bool is_neg, int64_t *val)
/* Determine result */
if ((k - i) > 16)
- rc = ATOI_OVERFLOW;
+ return -1;
else if (u > INT64_MAX)
- rc = ATOI_UNSIGNED;
+ *is_unsigned = true;
else
- rc = ATOI_SIGNED;
+ *is_unsigned = false;
}
- else
- rc = sql_atoi64(z, val, sqlStrlen30(z));
+ else if (sql_atoi64(z, val, is_unsigned, sqlStrlen30(z)) != 0)
+ return -1;
/* Apply sign */
if (is_neg) {
- switch (rc) {
- case ATOI_SIGNED:
- *val = -*val;
- break;
- case ATOI_OVERFLOW:
- /* n/a */
- break;
- case ATOI_UNSIGNED:
+ if (*is_unsigned){
/* A special processing is required
* for the INT64_MIN value. Any other
* values can't be presented as signed,
* so change the return value to error. */
if (*val == INT64_MIN)
- rc = ATOI_SIGNED;
+ *is_unsigned = false;
else
- rc = ATOI_OVERFLOW;
- break;
+ return -1; /* exceeds the i64 */
+
+ } else{
+ *val = -*val;
}
}
- return rc;
+ return 0;
}
/*
@@ -1296,17 +1289,16 @@ sqlAddInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB)
bool is_negA = iA < 0 && is_signedA;
bool is_negB = iB < 0 && is_signedB;
- /* Make sure we've got only one combination of
- * positive and negative operands
- */
- if (is_negA > is_negB) {
- SWAP(is_negA, is_negB);
- SWAP(iA, iB);
- }
-
if (is_negA != is_negB) {
+ /* Make sure we've got only one combination of
+ * positive and negative operands
+ */
+ if (is_negA > is_negB) {
+ SWAP(is_negA, is_negB);
+ SWAP(iA, iB);
+ }
+ assert(is_negA == false && is_negB == true);
- assert(iA >=0 && iB < 0);
u64 uB = mod64(iB, true);
if ((u64)iA >= uB) {
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 997d0a1ab..3df5006c8 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -295,9 +295,12 @@ mem_apply_numeric_type(Mem *pRec, int bTryForInt)
i64 iValue;
assert((pRec->flags & (MEM_Str|MEM_Int|MEM_Real))==MEM_Str);
if (sqlAtoF(pRec->z, &rValue, pRec->n) == 0) return -1;
- if (0 == sql_atoi64(pRec->z, (int64_t *)&iValue, pRec->n)) {
+ bool is_unsigned = false;
+ if (0 == sql_atoi64(pRec->z, (int64_t *)&iValue,
+ &is_unsigned, pRec->n)) {
pRec->u.i = iValue;
- pRec->flags |= MEM_Int;
+ pRec->flags |= is_unsigned ? (MEM_Int | MEM_Unsigned)
+ : MEM_Int;
} else {
pRec->u.r = rValue;
pRec->flags |= MEM_Real;
@@ -410,14 +413,12 @@ static u32 SQL_NOINLINE computeNumericType(Mem *pMem)
assert((pMem->flags & (MEM_Str|MEM_Blob))!=0);
if (sqlAtoF(pMem->z, &pMem->u.r, pMem->n)==0)
return 0;
- switch(sql_atoi64(pMem->z, (int64_t *)&pMem->u.i, pMem->n)) {
- case ATOI_SIGNED:
- return MEM_Int;
- case ATOI_UNSIGNED:
- return MEM_Int | MEM_Unsigned;
- default: /* ATOI_OVERFLOW:*/
- return MEM_Real;
- }
+ bool is_unsigned = false;
+ if (sql_atoi64(pMem->z, (int64_t *)&pMem->u.i,
+ &is_unsigned, pMem->n) != 0)
+ return MEM_Real;
+ return is_unsigned ? (MEM_Int | MEM_Unsigned)
+ : MEM_Int;
}
/*
@@ -1446,7 +1447,10 @@ case OP_IntCopy: { /* out2 */
pIn1 = &aMem[pOp->p1];
assert((pIn1->flags & MEM_Int)!=0);
pOut = &aMem[pOp->p2];
- sqlVdbeMemSetInt64(pOut, pIn1->u.i, (pIn1->flags & MEM_Unsigned)!=0);
+ if (pIn1->flags & MEM_Unsigned)
+ sqlVdbeMemSetUInt64(pOut, pIn1->u.u);
+ else
+ sqlVdbeMemSetInt64(pOut, pIn1->u.i);
break;
}
@@ -1778,7 +1782,7 @@ integer_overflow:
case OP_CollSeq: {
assert(pOp->p4type==P4_COLLSEQ || pOp->p4.pColl == NULL);
if (pOp->p1) {
- sqlVdbeMemSetInt64(&aMem[pOp->p1], 0, false);
+ sqlVdbeMemSetInt64(&aMem[pOp->p1], 0);
}
break;
}
@@ -5336,7 +5340,7 @@ case OP_AggStep: {
if (pCtx->skipFlag) {
assert(pOp[-1].opcode==OP_CollSeq);
i = pOp[-1].p1;
- if (i) sqlVdbeMemSetInt64(&aMem[i], 1, false);
+ if (i) sqlVdbeMemSetInt64(&aMem[i], 1);
}
break;
}
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index 46094929f..f986d136c 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 */
+ u64 u; /* Integer value used when MEM_UInt is set in flags */
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 */
@@ -479,7 +480,8 @@ 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, bool);
+void sqlVdbeMemSetInt64(Mem *, i64);
+void sqlVdbeMemSetUInt64(Mem *, u64);
#ifdef SQL_OMIT_FLOATING_POINT
#define sqlVdbeMemSetDouble sqlVdbeMemSetInt64
#else
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index d8f9d9b87..66fceb444 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -229,6 +229,15 @@ sql_value_int64(sql_value * pVal)
return i;
}
+sql_uint64
+sql_value_uint64(sql_value * pVal)
+{
+ uint64_t i;
+ bool is_unsigned = false;
+ sqlVdbeIntValue((Mem *) pVal, (int64_t *)&i, &is_unsigned);
+ return i;
+}
+
enum sql_subtype
sql_value_subtype(sql_value * pVal)
{
@@ -445,19 +454,22 @@ 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, false);
+ sqlVdbeMemSetInt64(pCtx->pOut, (i64) iVal);
}
void
sql_result_int64(sql_context * pCtx, i64 iVal)
{
- sqlVdbeMemSetInt64(pCtx->pOut, iVal, false);
+ sqlVdbeMemSetInt64(pCtx->pOut, iVal);
}
void
sql_result_uint64(sql_context * pCtx, u64 iVal)
{
- sqlVdbeMemSetInt64(pCtx->pOut, iVal, iVal > INT64_MAX);
+ if (iVal > INT64_MAX)
+ sqlVdbeMemSetUInt64(pCtx->pOut, iVal);
+ else
+ sqlVdbeMemSetInt64(pCtx->pOut, (i64)iVal);
}
void
@@ -1066,6 +1078,14 @@ sql_column_int64(sql_stmt * pStmt, int i)
return val;
}
+sql_uint64
+sql_column_uint64(sql_stmt * pStmt, int i)
+{
+ sql_uint64 val = sql_value_uint64(columnMem(pStmt, i));
+ columnMallocFailure(pStmt);
+ return val;
+}
+
const unsigned char *
sql_column_text(sql_stmt * pStmt, int i)
{
@@ -1412,7 +1432,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, false);
+ sqlVdbeMemSetInt64(&p->aVar[i - 1], iValue);
}
return rc;
}
@@ -1425,7 +1445,7 @@ sql_bind_uint64(sql_stmt * pStmt, int i, sql_uint64 iValue)
rc = vdbeUnbind(p, i);
if (rc == SQL_OK) {
rc = sql_bind_type(p, i, "INTEGER");
- sqlVdbeMemSetInt64(&p->aVar[i - 1], (u64)iValue, true);
+ sqlVdbeMemSetUInt64(&p->aVar[i - 1], iValue);
}
return rc;
}
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index e4ea987cb..2d61ca675 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -431,7 +431,7 @@ doubleToInt64(double r, int64_t *i, bool* is_unsigned)
*i = maxUInt;
*is_unsigned = true;
return -1;
- } else if (r > (double)maxInt){
+ } else if (r >= (double)maxInt){
uint64_t t = (uint64_t) r;
*i = (int64_t) t;
*is_unsigned = true;
@@ -468,17 +468,7 @@ sqlVdbeIntValue(Mem * pMem, int64_t *i, bool *is_unsigned)
return doubleToInt64(pMem->u.r, i, is_unsigned);
} else if (flags & (MEM_Str)) {
assert(pMem->z || pMem->n == 0);
- enum atoi_result rc = sql_atoi64(pMem->z, i, pMem->n);
- switch(rc) {
- case ATOI_SIGNED:
- *is_unsigned = false;
- return 0;
- case ATOI_UNSIGNED:
- *is_unsigned = true;
- return 0;
- default:
- return -1;
- }
+ return sql_atoi64(pMem->z, i, is_unsigned, pMem->n);
}
return -1;
}
@@ -601,8 +591,15 @@ sqlVdbeMemNumerify(Mem * pMem)
{
if ((pMem->flags & (MEM_Int | MEM_Real | MEM_Null)) == 0) {
assert((pMem->flags & (MEM_Blob | MEM_Str)) != 0);
- if (0 == sql_atoi64(pMem->z, (int64_t *)&pMem->u.i, pMem->n)) {
- MemSetTypeFlag(pMem, MEM_Int);
+ bool is_unsigned = false;
+ if (0 == sql_atoi64(pMem->z,
+ (int64_t *)&pMem->u.i,
+ &is_unsigned,
+ pMem->n)) {
+ if (is_unsigned)
+ MemSetTypeFlag(pMem, MEM_Int | MEM_Unsigned);
+ else
+ MemSetTypeFlag(pMem, MEM_Int);
} else {
double v;
if (sqlVdbeRealValue(pMem, &v))
@@ -631,9 +628,15 @@ sqlVdbeMemCast(Mem * pMem, enum field_type type)
if (pMem->flags & MEM_Null)
return SQL_OK;
if ((pMem->flags & MEM_Blob) != 0 && type == FIELD_TYPE_NUMBER) {
- if (sql_atoi64(pMem->z, (int64_t *) &pMem->u.i, pMem->n) == 0) {
+ bool is_unsigned = false;
+ if (sql_atoi64(pMem->z, (int64_t *) &pMem->u.i,
+ &is_unsigned, pMem->n) == 0) {
MemSetTypeFlag(pMem, MEM_Real);
- pMem->u.r = pMem->u.i;
+
+ if (is_unsigned)
+ pMem->u.r = (u64)pMem->u.i;
+ else
+ pMem->u.r = pMem->u.i;
return 0;
}
return ! sqlAtoF(pMem->z, &pMem->u.r, pMem->n);
@@ -643,10 +646,15 @@ sqlVdbeMemCast(Mem * pMem, enum field_type type)
return 0;
case FIELD_TYPE_INTEGER:
if ((pMem->flags & MEM_Blob) != 0) {
+ bool is_unsigned = false;
if (sql_atoi64(pMem->z, (int64_t *) &pMem->u.i,
+ &is_unsigned,
pMem->n) != 0)
return -1;
- MemSetTypeFlag(pMem, MEM_Int);
+ if (is_unsigned)
+ MemSetTypeFlag(pMem, MEM_Int | MEM_Unsigned);
+ else
+ MemSetTypeFlag(pMem, MEM_Int);
return 0;
}
return sqlVdbeMemIntegerify(pMem, true);
@@ -727,13 +735,19 @@ sqlVdbeMemSetZeroBlob(Mem * pMem, int n)
* a 64-bit integer.
*/
static SQL_NOINLINE void
-vdbeReleaseAndSetInt64(Mem * pMem, i64 val, bool is_unsigned)
+vdbeReleaseAndSetInt64(Mem * pMem, i64 val)
{
sqlVdbeMemSetNull(pMem);
pMem->u.i = val;
pMem->flags = MEM_Int;
- if (is_unsigned)
- pMem->flags |= MEM_Unsigned;
+}
+
+static SQL_NOINLINE void
+vdbeReleaseAndSetUInt64(Mem * pMem, u64 val)
+{
+ sqlVdbeMemSetNull(pMem);
+ pMem->u.u = val;
+ pMem->flags = MEM_Int | MEM_Unsigned;
}
/*
@@ -741,15 +755,24 @@ vdbeReleaseAndSetInt64(Mem * pMem, i64 val, bool is_unsigned)
* manifest type INTEGER.
*/
void
-sqlVdbeMemSetInt64(Mem * pMem, i64 val, bool is_unsigned)
+sqlVdbeMemSetInt64(Mem * pMem, i64 val)
{
if (VdbeMemDynamic(pMem)) {
- vdbeReleaseAndSetInt64(pMem, val, is_unsigned);
+ vdbeReleaseAndSetInt64(pMem, val);
} else {
pMem->u.i = val;
pMem->flags = MEM_Int;
- if (is_unsigned)
- pMem->flags |= MEM_Unsigned;
+ }
+}
+
+void
+sqlVdbeMemSetUInt64(Mem * pMem, u64 val)
+{
+ if (VdbeMemDynamic(pMem)) {
+ vdbeReleaseAndSetUInt64(pMem, val);
+ } else {
+ pMem->u.u = val;
+ pMem->flags = MEM_Int | MEM_Unsigned;
}
}
@@ -1325,7 +1348,7 @@ valueFromExpr(sql * db, /* The database connection */
goto no_mem;
if (ExprHasProperty(pExpr, EP_IntValue)) {
sqlVdbeMemSetInt64(pVal,
- (i64) pExpr->u.iValue * negInt, false);
+ (i64) pExpr->u.iValue * negInt);
} else {
zVal =
sqlMPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 8e75f9c89..c77b183db 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -919,7 +919,7 @@ test:do_execsql_test(
UNION ALL SELECT -9223372036854775807)
]], {
-- <func-8.7>
- "real"
+ "integer"
-- </func-8.7>
})
diff --git a/test/sql/integer-overflow.result b/test/sql/integer-overflow.result
index b6cacbeff..8dd3a05dd 100644
--- a/test/sql/integer-overflow.result
+++ b/test/sql/integer-overflow.result
@@ -44,7 +44,7 @@ box.sql.execute('SELECT 9223372036854775808 - 1;')
--
box.sql.execute('SELECT CAST(\'9223372036854775808\' AS INTEGER);')
---
-- error: 'Type mismatch: can not convert 9223372036854775808 to integer'
+- - [9223372036854775808]
...
-- Due to inexact represantation of large integers in terms of
-- floating point numbers, numerics with value < INT64_MAX
@@ -54,7 +54,7 @@ box.sql.execute('SELECT CAST(\'9223372036854775808\' AS INTEGER);')
--
box.sql.execute('SELECT CAST(9223372036854775807.0 AS INTEGER);')
---
-- error: 'Type mismatch: can not convert 9.22337203685478e+18 to integer'
+- - [9223372036854775808]
...
-- gh-3810: make sure that if space contains integers in range
-- [INT64_MAX, UINT64_MAX], they are handled inside SQL in a
--
2.17.1
More information about the Tarantool-patches
mailing list