* [tarantool-patches] [PATCH v3 1/2] sql: support -2^63 .. 2^64-1 integer type
2019-04-03 11:47 [tarantool-patches] [PATCH v3 0/2] sql: support -2^63 .. 2^64-1 integer type *** Stanislav Zudin
@ 2019-04-03 11:47 ` Stanislav Zudin
2019-04-04 0:49 ` [tarantool-patches] " n.pettik
2019-04-03 11:47 ` [tarantool-patches] [PATCH v3 2/2] sql: removes unused function Stanislav Zudin
1 sibling, 1 reply; 4+ messages in thread
From: Stanislav Zudin @ 2019-04-03 11:47 UTC (permalink / raw)
To: tarantool-patches, korablev; +Cc: Stanislav Zudin
Makes sql functions avg() and sum() accept arguments with
values in the range [2^63, 2^64).
Makes arithmetic functions accept arguments with
values in the range [2^63, 2^64).
Correctly handles the signed and unsigned integers on the way from sql
expression to VDBE.
The cast operations take the unsigned into account.
Adapts auxiliary functions for supporting
arithmetic operations with unsigned integers.
SELECT query correctly returns values greater
than INT64_MAX.
Enables sql binding for unsigned int64.
Returns correct text name for unsigned data type.
Improves mapping of vdbe data types to sql data types.
Adds functions setting and retrieving unsigned value.
atoi functions return 0 on success and -1 on error
and return the signed/unsigned property by pointer.
Part of #3810
---
src/box/execute.c | 24 +-
src/box/lua/lua_sql.c | 3 +
src/box/lua/sql.c | 3 +
src/box/sql/build.c | 4 +-
src/box/sql/expr.c | 32 +-
src/box/sql/func.c | 55 ++-
src/box/sql/main.c | 2 +-
src/box/sql/sqlInt.h | 81 +++--
src/box/sql/util.c | 395 +++++++++++++--------
src/box/sql/vdbe.c | 171 ++++++---
src/box/sql/vdbe.h | 3 +-
src/box/sql/vdbeInt.h | 14 +-
src/box/sql/vdbeapi.c | 116 ++++--
src/box/sql/vdbeaux.c | 108 +++++-
src/box/sql/vdbemem.c | 153 +++++---
src/box/sql/vdbetrace.c | 2 +
test/sql-tap/func.test.lua | 22 +-
test/sql-tap/hexlit.test.lua | 6 +-
test/sql/gh-2347-max-int-literals.result | 11 +-
test/sql/gh-2347-max-int-literals.test.lua | 4 +
test/sql/integer-overflow.result | 18 +-
test/sql/iproto.result | 11 +-
test/sql/iproto.test.lua | 5 +-
23 files changed, 867 insertions(+), 376 deletions(-)
diff --git a/src/box/execute.c b/src/box/execute.c
index 7c77df2e5..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,14 +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);
- if (n > INT64_MAX) {
- diag_set(ClientError, ER_SQL_BIND_VALUE,
- sql_bind_name(bind), "INTEGER");
- return -1;
- }
- bind->i64 = (int64_t) n;
- bind->type = SQL_INTEGER;
- bind->bytes = sizeof(bind->i64);
+ bind->u64 = n;
+ bind->type = (n > INT64_MAX) ? SQL_UNSIGNED : SQL_INTEGER;
+ bind->bytes = sizeof(bind->u64);
break;
}
case MP_INT:
@@ -260,6 +256,15 @@ sql_column_to_messagepack(struct sql_stmt *stmt, int i,
mp_encode_int(pos, n);
break;
}
+ case SQL_UNSIGNED: {
+ uint64_t n = sql_column_uint64(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 SQL_FLOAT: {
double d = sql_column_double(stmt, i);
size = mp_sizeof_double(d);
@@ -389,6 +394,9 @@ sql_bind_column(struct sql_stmt *stmt, const struct sql_bind *p,
case SQL_INTEGER:
rc = sql_bind_int64(stmt, pos, p->i64);
break;
+ case SQL_UNSIGNED:
+ rc = sql_bind_uint64(stmt, pos, p->u64);
+ break;
case SQL_FLOAT:
rc = sql_bind_double(stmt, pos, p->d);
break;
diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c
index f5a7b7819..f544c078d 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 SQL_INTEGER:
luaL_pushint64(L, sql_value_int64(param));
break;
+ case SQL_UNSIGNED:
+ luaL_pushuint64(L, sql_value_uint64(param));
+ break;
case SQL_FLOAT:
lua_pushnumber(L, sql_value_double(param));
break;
diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c
index ee20faab7..e7872c716 100644
--- a/src/box/lua/sql.c
+++ b/src/box/lua/sql.c
@@ -34,6 +34,9 @@ lua_push_row(struct lua_State *L, struct sql_stmt *stmt)
case SQL_INTEGER:
luaL_pushint64(L, sql_column_int64(stmt, i));
break;
+ case SQL_UNSIGNED:
+ luaL_pushuint64(L, sql_column_uint64(stmt, i));
+ break;
case SQL_FLOAT:
lua_pushnumber(L, sql_column_double(stmt, i));
break;
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index e9851d9a1..34fd03862 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -939,10 +939,10 @@ emitNewSysSequenceRecord(Parse *pParse, int reg_seq_id, const char *seq_name)
/* 5. Minimum */
sqlVdbeAddOp4Dup8(v, OP_Int64, 0, first_col + 5, 0,
- (unsigned char*)&min_usigned_long_long, P4_INT64);
+ (unsigned char*)&min_usigned_long_long, P4_UINT64);
/* 6. Maximum */
sqlVdbeAddOp4Dup8(v, OP_Int64, 0, first_col + 6, 0,
- (unsigned char*)&max_usigned_long_long, P4_INT64);
+ (unsigned char*)&max_usigned_long_long, P4_UINT64);
/* 7. Start */
sqlVdbeAddOp2(v, OP_Integer, 1, first_col + 7);
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 82688dff3..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,23 +3337,29 @@ expr_code_int(struct Parse *parse, struct Expr *expr, bool is_neg,
int64_t value;
const char *z = expr->u.zToken;
assert(z != NULL);
- int c = sql_dec_or_hex_to_i64(z, &value);
- if (c == 1 || (c == 2 && !is_neg) ||
- (is_neg && value == SMALLEST_INT64)) {
+ 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",
- is_neg ? "-" : "", z);
+ "hex literal too big: %s%s",
+ is_neg ? "-" : "", z);
} else {
sqlErrorMsg(parse,
- "oversized integer: %s%s",
- is_neg ? "-" : "", z);
+ "oversized integer: %s%s",
+ is_neg ? "-" : "", z);
}
} else {
- if (is_neg)
- value = c == 2 ? SMALLEST_INT64 : -value;
- sqlVdbeAddOp4Dup8(v, OP_Int64, 0, mem, 0,
- (u8 *)&value, P4_INT64);
+ 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 21a69aa51..74b56d780 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -121,6 +121,9 @@ typeofFunc(sql_context * context, int NotUsed, sql_value ** argv)
case SQL_BLOB:
z = "blob";
break;
+ case SQL_UNSIGNED:
+ z = "unsigned";
+ break;
default:
z = "null";
break;
@@ -141,6 +144,7 @@ lengthFunc(sql_context * context, int argc, sql_value ** argv)
switch (sql_value_type(argv[0])) {
case SQL_BLOB:
case SQL_INTEGER:
+ case SQL_UNSIGNED:
case SQL_FLOAT:{
sql_result_int(context,
sql_value_bytes(argv[0]));
@@ -191,6 +195,11 @@ absFunc(sql_context * context, int argc, sql_value ** argv)
sql_result_int64(context, iVal);
break;
}
+ case SQL_UNSIGNED: {
+ u64 iVal = sql_value_uint64(argv[0]);
+ sql_result_int64(context, iVal);
+ break;
+ }
case SQL_NULL:{
/* IMP: R-37434-19929 Abs(X) returns NULL if X is NULL. */
sql_result_null(context);
@@ -957,6 +966,7 @@ quoteFunc(sql_context * context, int argc, sql_value ** argv)
SQL_TRANSIENT);
break;
}
+ case SQL_UNSIGNED:
case SQL_INTEGER:{
sql_result_value(context, argv[0]);
break;
@@ -1403,6 +1413,7 @@ struct SumCtx {
i64 cnt; /* Number of elements summed */
u8 overflow; /* True if integer overflow seen */
u8 approx; /* True if non-integer value was input to the sum */
+ bool is_unsigned; /* True if value exceeded 2^63 */
};
/*
@@ -1426,16 +1437,40 @@ sumStep(sql_context * context, int argc, sql_value ** argv)
type = sql_value_numeric_type(argv[0]);
if (p && type != SQL_NULL) {
p->cnt++;
+ enum arithmetic_result rc = ATHR_SIGNED;
+
if (type == SQL_INTEGER) {
i64 v = sql_value_int64(argv[0]);
p->rSum += v;
- if ((p->approx | p->overflow) == 0
- && sqlAddInt64(&p->iSum, v)) {
- p->overflow = 1;
- }
+ if ((p->approx | p->overflow) == 0)
+ rc = sqlAddInt64(&p->iSum,
+ !p->is_unsigned,
+ v, true);
+ } else if (type == SQL_UNSIGNED) {
+ u64 v = sql_value_uint64(argv[0]);
+ p->rSum += v;
+ if ((p->approx | p->overflow) == 0)
+ rc = sqlAddInt64(&p->iSum,
+ !p->is_unsigned,
+ v, false);
} else {
p->rSum += sql_value_double(argv[0]);
p->approx = 1;
+ return;
+ }
+
+ /* process the result of integer addition */
+ if ((p->approx | p->overflow) == 0) {
+ switch (rc) {
+ case ATHR_SIGNED:
+ break;
+ case ATHR_UNSIGNED:
+ p->is_unsigned = true;
+ break;
+ default:
+ p->overflow = 1;
+ break;
+ }
}
}
}
@@ -1448,6 +1483,8 @@ sumFinalize(sql_context * context)
if (p && p->cnt > 0) {
if (p->overflow) {
sql_result_error(context, "integer overflow", -1);
+ } else if (p->is_unsigned) {
+ sql_result_uint64(context, (u64)p->iSum);
} else if (p->approx) {
sql_result_double(context, p->rSum);
} else {
@@ -1462,7 +1499,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/main.c b/src/box/sql/main.c
index 0b3bd201e..9fe2e2c9d 100644
--- a/src/box/sql/main.c
+++ b/src/box/sql/main.c
@@ -1920,7 +1920,7 @@ sql_uri_int64(const char *zFilename, /* Filename as passed to xOpen */
{
const char *z = sql_uri_parameter(zFilename, zParam);
int64_t v;
- if (z != NULL && sql_dec_or_hex_to_i64(z, &v) == 0)
+ if (z != NULL && sql_dec_or_hex_to_i64(z, false, &v) == 0)
bDflt = v;
return bDflt;
}
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index eb1488576..8942addd3 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 *);
@@ -495,6 +498,9 @@ sql_result_int(sql_context *, int);
void
sql_result_int64(sql_context *, sql_int64);
+void
+sql_result_uint64(sql_context *, sql_uint64);
+
void
sql_result_null(sql_context *);
@@ -579,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);
@@ -641,6 +650,7 @@ enum sql_type {
SQL_TEXT = 3,
SQL_BLOB = 4,
SQL_NULL = 5,
+ SQL_UNSIGNED = 6
};
/**
@@ -903,6 +913,9 @@ sql_bind_int(sql_stmt *, int, int);
int
sql_bind_int64(sql_stmt *, int, sql_int64);
+int
+sql_bind_uint64(sql_stmt *, int, sql_uint64);
+
int
sql_bind_null(sql_stmt *, int);
@@ -4275,20 +4288,20 @@ field_type_sequence_dup(struct Parse *parse, enum field_type *types,
uint32_t len);
/**
- * Convert z to a 64-bit signed integer. z must be decimal. This
- * routine does *not* accept hexadecimal notation.
+ * 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 0.
*
- * If z is exactly 9223372036854775808, return 2. This special
- * case is broken out because while 9223372036854775808 cannot be
- * a signed 64-bit integer, its negative -9223372036854775808 can
- * be.
+ * If z is a number in the range
+ * [9223372036854775808, 18446744073709551615] function returns
+ * 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 1.
+ * 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.
@@ -4296,16 +4309,15 @@ field_type_sequence_dup(struct Parse *parse, enum field_type *types,
*
* @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
- * 0 Successful transformation. Fits in a 64-bit signed
- * integer.
- * 1 Integer too large for a 64-bit signed integer or is
- * malformed
- * 2 Special case of 9223372036854775808
+ * 0 Successful transformation.
+ * -1 An error occurred.
*/
int
-sql_atoi64(const char *z, int64_t *val, int length);
+sql_atoi64(const char *z, int64_t *val, bool *is_unsigned, int length);
/**
* Transform a UTF-8 integer literal, in either decimal or
@@ -4313,14 +4325,17 @@ sql_atoi64(const char *z, int64_t *val, int length);
* accepts hexadecimal literals, whereas sql_atoi64() does not.
*
* @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
- * 0 Successful transformation. Fits in a 64-bit signed integer.
- * 1 Integer too large for a 64-bit signed integer or is malformed
- * 2 Special case of 9223372036854775808
+ * 0 Successful transformation.
+ * -1 An error occurred.
*/
int
-sql_dec_or_hex_to_i64(const char *z, int64_t *val);
+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);
@@ -4358,9 +4373,31 @@ Expr *sqlExprAddCollateString(Parse *, Expr *, const char *);
Expr *sqlExprSkipCollate(Expr *);
int sqlCheckIdentifierName(Parse *, char *);
void sqlVdbeSetChanges(sql *, int);
-int sqlAddInt64(i64 *, i64);
-int sqlSubInt64(i64 *, i64);
-int sqlMulInt64(i64 *, i64);
+
+enum arithmetic_result {
+ /* The result fits the signed 64-bit integer */
+ ATHR_SIGNED,
+ /* The result is positive and fits the
+ * unsigned 64-bit integer
+ */
+ ATHR_UNSIGNED,
+ /* The operation causes an overflow */
+ ATHR_OVERFLOW,
+ /* The operation causes division by zero */
+ ATHR_DIVBYZERO
+};
+
+enum arithmetic_result
+sqlAddInt64(i64 *, bool, i64, bool);
+enum arithmetic_result
+sqlSubInt64(i64 *, bool, i64, bool);
+enum arithmetic_result
+sqlMulInt64(i64 *, bool, i64, bool);
+enum arithmetic_result
+sqlDivInt64(i64 *, bool, i64, bool);
+enum arithmetic_result
+sqlRemInt64(i64 *, bool, i64, bool);
+
int sqlAbsInt32(int);
#ifdef SQL_ENABLE_8_3_NAMES
void sqlFileSuffix3(const char *, char *);
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index c89e2e8ab..d1b159770 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -591,109 +591,55 @@ sqlAtoF(const char *z, double *pResult, int length)
#endif /* SQL_OMIT_FLOATING_POINT */
}
-/*
- * Compare the 19-character string zNum against the text representation
- * value 2^63: 9223372036854775808. Return negative, zero, or positive
- * if zNum is less than, equal to, or greater than the string.
- * Note that zNum must contain exactly 19 characters.
- *
- * Unlike memcmp() this routine is guaranteed to return the difference
- * in the values of the last digit if the only difference is in the
- * last digit. So, for example,
- *
- * compare2pow63("9223372036854775800", 1)
- *
- * will return -8.
- */
-static int
-compare2pow63(const char *zNum, int incr)
-{
- int c = 0;
- int i;
- /* 012345678901234567 */
- const char *pow63 = "922337203685477580";
- for (i = 0; c == 0 && i < 18; i++) {
- c = (zNum[i * incr] - pow63[i]) * 10;
- }
- if (c == 0) {
- c = zNum[18 * incr] - '8';
- testcase(c == (-1));
- testcase(c == 0);
- testcase(c == (+1));
- }
- return c;
-}
+#ifndef INT64_MIN_MOD
+/* Modulo of INT64_MIN */
+#define INT64_MIN_MOD 0x8000000000000000
+#endif
int
-sql_atoi64(const char *z, int64_t *val, int length)
+sql_atoi64(const char *z, int64_t *val, bool *is_unsigned, int length)
{
- int incr = 1;
- u64 u = 0;
+ const char* expected_end = z + length;
int neg = 0; /* assume positive */
- int i;
- int c = 0;
- int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
- const char *zStart;
const char *zEnd = z + length;
- incr = 1;
+ int incr = 1;
while (z < zEnd && sqlIsspace(*z))
z += incr;
- if (z < zEnd) {
- if (*z == '-') {
- neg = 1;
- z += incr;
- } else if (*z == '+') {
- z += incr;
- }
- }
- zStart = z;
- /* Skip leading zeros. */
- while (z < zEnd && z[0] == '0') {
+
+ if (z >= zEnd)
+ return -1; /* invalid format */
+ if (*z == '-') {
+ neg = 1;
z += incr;
}
- for (i = 0; &z[i] < zEnd && (c = z[i]) >= '0' && c <= '9';
- i += incr) {
- u = u * 10 + c - '0';
- }
- if (u > LARGEST_INT64) {
- *val = neg ? SMALLEST_INT64 : LARGEST_INT64;
- } else if (neg) {
- *val = -(i64) u;
+
+ char* end = NULL;
+ errno = 0;
+ u64 u = strtoull(z, &end, 10);
+ if (end < expected_end)
+ return -1;
+ if (errno != 0)
+ return -1;
+
+ if (neg) {
+ *is_unsigned = false;
+ if (u <= INT64_MAX)
+ *val = -u;
+ else if (u == INT64_MIN_MOD)
+ *val = (i64) u;
+ else
+ return -1;
} else {
*val = (i64) u;
+ *is_unsigned = (u > INT64_MAX);
}
- if (&z[i] < zEnd || (i == 0 && zStart == z) || i > 19 * incr ||
- nonNum) {
- /* zNum is empty or contains non-numeric text or is longer
- * than 19 digits (thus guaranteeing that it is too large)
- */
- return 1;
- } else if (i < 19 * incr) {
- /* Less than 19 digits, so we know that it fits in 64 bits */
- assert(u <= LARGEST_INT64);
- return 0;
- } else {
- /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
- c = compare2pow63(z, incr);
- if (c < 0) {
- /* zNum is less than 9223372036854775808 so it fits */
- assert(u <= LARGEST_INT64);
- return 0;
- } else if (c > 0) {
- /* zNum is greater than 9223372036854775808 so it overflows */
- return 1;
- } else {
- /* zNum is exactly 9223372036854775808. Fits if negative. The
- * special case 2 overflow if positive
- */
- assert(u - 1 == LARGEST_INT64);
- return neg ? 0 : 2;
- }
- }
+
+ return 0;
}
int
-sql_dec_or_hex_to_i64(const char *z, int64_t *val)
+sql_dec_or_hex_to_i64(const char *z, bool is_neg, int64_t *val,
+ bool *is_unsigned)
{
if (z[0] == '0' && (z[1] == 'x' || z[1] == 'X')) {
uint64_t u = 0;
@@ -702,9 +648,36 @@ sql_dec_or_hex_to_i64(const char *z, int64_t *val)
for (k = i; sqlIsxdigit(z[k]); k++)
u = u * 16 + sqlHexToInt(z[k]);
memcpy(val, &u, 8);
- return (z[k] == 0 && k - i <= 16) ? 0 : 1;
+
+ /* Determine result */
+ if ((k - i) > 16)
+ return -1;
+ else if (u > INT64_MAX)
+ *is_unsigned = true;
+ else
+ *is_unsigned = false;
}
- return sql_atoi64(z, val, sqlStrlen30(z));
+ else if (sql_atoi64(z, val, is_unsigned, sqlStrlen30(z)) != 0)
+ return -1;
+
+ /* Apply sign */
+ if (is_neg) {
+ 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)
+ *is_unsigned = false;
+ else
+ return -1; /* exceeds the i64 */
+
+ } else{
+ *val = -*val;
+ }
+ }
+
+ return 0;
}
/*
@@ -1271,74 +1244,206 @@ sqlSafetyCheckSickOrOk(sql * db)
}
/*
- * Attempt to add, substract, or multiply the 64-bit signed value iB against
- * the other 64-bit signed integer at *pA and store the result in *pA.
- * Return 0 on success. Or if the operation would have resulted in an
- * overflow, leave *pA unchanged and return 1.
+ * Get modulo of 64-bit number
*/
-int
-sqlAddInt64(i64 * pA, i64 iB)
+static u64 mod64(i64 v, bool is_signed)
{
- i64 iA = *pA;
- testcase(iA == 0);
- testcase(iA == 1);
- testcase(iB == -1);
- testcase(iB == 0);
- if (iB >= 0) {
- testcase(iA > 0 && LARGEST_INT64 - iA == iB);
- testcase(iA > 0 && LARGEST_INT64 - iA == iB - 1);
- if (iA > 0 && LARGEST_INT64 - iA < iB)
- return 1;
- } else {
- testcase(iA < 0 && -(iA + LARGEST_INT64) == iB + 1);
- testcase(iA < 0 && -(iA + LARGEST_INT64) == iB + 2);
- if (iA < 0 && -(iA + LARGEST_INT64) > iB + 1)
- return 1;
+ bool is_neg = v < 0 && is_signed;
+ if (is_neg)
+ return (v == INT64_MIN) ? (u64)v : (u64)(-v);
+ else
+ return (u64)v;
+}
+
+static enum arithmetic_result
+apply_sign(i64* pOut, u64 value, bool is_neg)
+{
+ if (is_neg) {
+ if (value > INT64_MIN_MOD)
+ return ATHR_OVERFLOW;
+ else if (value == INT64_MIN_MOD)
+ *pOut = (i64)value;
+ else
+ *pOut = -(i64)value;
+
+ return ATHR_SIGNED;
}
- *pA += iB;
- return 0;
+
+ *pOut = (i64) value;
+ return (value > INT64_MAX) ? ATHR_UNSIGNED
+ : ATHR_SIGNED;
}
-int
-sqlSubInt64(i64 * pA, i64 iB)
+/*
+ * Attempt to add, substract, or multiply the 64-bit value irhs against
+ * the other 64-bit integer at *plhs and store the result in *plhs.
+ * Return ATHR_SIGNED or ATHR_UNSIGNED on success.
+ * Or if the operation would have resulted in an
+ * overflow, leave *pA unchanged and return ATHR_OVERFLOW.
+ */
+enum arithmetic_result
+sqlAddInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed)
{
- testcase(iB == SMALLEST_INT64 + 1);
- if (iB == SMALLEST_INT64) {
- testcase((*pA) == (-1));
- testcase((*pA) == 0);
- if ((*pA) >= 0)
- return 1;
- *pA -= iB;
- return 0;
+ i64 ilhs = *plhs;
+
+ bool is_lhs_neg = ilhs < 0 && is_lhs_signed;
+ bool is_rhs_neg = irhs < 0 && is_rhs_signed;
+
+ if (is_lhs_neg != is_rhs_neg) {
+ /*
+ * Make sure we've got only one combination of
+ * positive and negative operands
+ */
+ if (is_lhs_neg > is_rhs_neg) {
+ SWAP(is_lhs_neg, is_rhs_neg);
+ SWAP(ilhs, irhs);
+ }
+ assert(is_lhs_neg == false && is_rhs_neg == true);
+
+ u64 uB = mod64(irhs, true);
+
+ if ((u64)ilhs >= uB) {
+ u64 sum = (u64)ilhs - uB;
+ *plhs = (i64)sum;
+ return (sum <= INT64_MAX) ? ATHR_SIGNED
+ : ATHR_UNSIGNED;
+ } else {
+ u64 sum = uB - (u64)ilhs;
+ if (sum == INT64_MIN_MOD) {
+ *plhs = INT64_MIN;
+ } else {
+ assert(sum <= INT64_MAX);
+ *plhs = -(i64)sum;
+ }
+ return ATHR_SIGNED;
+ }
+ }
+
+ if (is_lhs_neg) {
+ assert(is_lhs_signed && is_rhs_signed);
+ if (-(ilhs + LARGEST_INT64) > irhs + 1)
+ return ATHR_OVERFLOW;
+ *plhs = ilhs + irhs;
+ return ATHR_SIGNED;
} else {
- return sqlAddInt64(pA, -iB);
+ if (UINT64_MAX - (u64)ilhs < (u64)irhs)
+ return ATHR_OVERFLOW;
+
+ u64 sum = (u64)ilhs + (u64)irhs;
+ *plhs = (i64)sum;
+ return (sum <= INT64_MAX) ? ATHR_SIGNED
+ : ATHR_UNSIGNED;
}
}
-int
-sqlMulInt64(i64 * pA, i64 iB)
+enum arithmetic_result
+sqlSubInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed)
{
- i64 iA = *pA;
- if (iB > 0) {
- if (iA > LARGEST_INT64 / iB)
- return 1;
- if (iA < SMALLEST_INT64 / iB)
- return 1;
- } else if (iB < 0) {
- if (iA > 0) {
- if (iB < SMALLEST_INT64 / iA)
- return 1;
- } else if (iA < 0) {
- if (iB == SMALLEST_INT64)
- return 1;
- if (iA == SMALLEST_INT64)
- return 1;
- if (-iA > LARGEST_INT64 / -iB)
- return 1;
+ i64 ilhs = *plhs;
+
+ bool is_lhs_neg = ilhs < 0 && is_lhs_signed;
+ bool is_rhs_neg = irhs < 0 && is_rhs_signed;
+
+ if (is_lhs_neg) {
+ if (!is_rhs_signed){
+ assert((u64)irhs > INT64_MAX);
+ return ATHR_OVERFLOW;
}
+
+ if (irhs == INT64_MIN)
+ return ATHR_OVERFLOW;
+ else
+ return sqlAddInt64(plhs, true, -irhs, true);
}
- *pA = iA * iB;
- return 0;
+
+ if (is_rhs_neg) {
+ /* ilhs - (-irhs) => ilhs + irhs */
+ u64 uB = mod64(irhs, true);
+ if (irhs == INT64_MIN)
+ is_rhs_signed = false;
+
+ return sqlAddInt64(plhs, is_lhs_signed, uB, is_rhs_signed);
+ } else {
+ /* Both ilhs & irhs are positive */
+ if ((u64)ilhs < (u64)irhs) {
+ /* subtract with sign changing */
+ u64 val = (u64)irhs - (u64)ilhs;
+ return apply_sign(plhs, val, true);
+ } else {
+ u64 val = (u64)ilhs - (u64)irhs;
+ *plhs = (i64)val;
+ return (val > INT64_MAX) ? ATHR_UNSIGNED
+ : ATHR_SIGNED;
+ }
+ }
+}
+
+enum arithmetic_result
+sqlMulInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed)
+{
+ if (*plhs == 0 || irhs == 0) {
+ *plhs = 0;
+ return ATHR_SIGNED;
+ }
+
+ bool is_lhs_neg = *plhs < 0 && is_lhs_signed;
+ bool is_rhs_neg = irhs < 0 && is_rhs_signed;
+
+ bool is_neg = is_lhs_neg != is_rhs_neg;
+
+ u64 ulhs = mod64(*plhs, is_lhs_signed);
+ u64 urhs = mod64(irhs, is_rhs_signed);
+
+ if (is_neg) {
+ if (INT64_MIN_MOD / ulhs < urhs)
+ return ATHR_OVERFLOW;
+ } else {
+ if (UINT64_MAX / ulhs < urhs)
+ return ATHR_OVERFLOW;
+ }
+
+ u64 mul = ulhs * urhs;
+ return apply_sign(plhs, mul, is_neg);
+}
+
+enum arithmetic_result
+sqlDivInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed) {
+ if (*plhs == 0)
+ return ATHR_SIGNED;
+ if (irhs == 0)
+ return ATHR_DIVBYZERO;
+
+ bool is_lhs_neg = *plhs < 0 && is_lhs_signed;
+ bool is_rhs_neg = irhs < 0 && is_rhs_signed;
+
+ bool is_neg = is_lhs_neg != is_rhs_neg;
+
+ u64 ulhs = mod64(*plhs, is_lhs_signed);
+ u64 urhs = mod64(irhs, is_rhs_signed);
+
+ u64 div = ulhs / urhs;
+ return apply_sign(plhs, div, is_neg);
+}
+
+enum arithmetic_result
+sqlRemInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed) {
+
+ if (irhs == 0)
+ return ATHR_DIVBYZERO;
+ /*
+ * The sign of the remainder is defined in such
+ * a way that if the quotient a/b is representable
+ * in the result type, then (a/b)*b + a%b == a.
+ *
+ * The 2nd operand doesn't affect the sign of result.
+ */
+
+ bool is_neg = *plhs < 0 && is_lhs_signed;
+ u64 ulhs = mod64(*plhs, is_lhs_signed);
+ u64 urhs = mod64(irhs, is_rhs_signed);
+
+ u64 rem = ulhs % urhs;
+ return apply_sign(plhs, rem, is_neg);
}
/*
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index c1da9a4aa..a148bb7d4 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -293,11 +293,14 @@ mem_apply_numeric_type(Mem *pRec, int bTryForInt)
{
double rValue;
i64 iValue;
- assert((pRec->flags & (MEM_Str|MEM_Int|MEM_Real))==MEM_Str);
+ assert((pRec->flags & (MEM_Str|MEM_Int|MEM_Real|MEM_UInt))==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_UInt
+ : MEM_Int;
} else {
pRec->u.r = rValue;
pRec->flags |= MEM_Real;
@@ -335,9 +338,13 @@ mem_apply_type(struct Mem *record, enum field_type type)
assert(type < field_type_MAX);
switch (type) {
case FIELD_TYPE_INTEGER:
- case FIELD_TYPE_UNSIGNED:
- if ((record->flags & MEM_Int) == MEM_Int)
+ if ((record->flags & MEM_Int) == MEM_Int) {
+ return 0;
+ }
+ if ((record->flags & MEM_UInt) == MEM_UInt) {
+ MemSetTypeFlag(record, MEM_Int);
return 0;
+ }
if ((record->flags & MEM_Real) == MEM_Real) {
int64_t i = (int64_t) record->u.r;
if (i == record->u.r) {
@@ -347,8 +354,24 @@ mem_apply_type(struct Mem *record, enum field_type type)
return 0;
}
return sqlVdbeMemIntegerify(record, false);
+ case FIELD_TYPE_UNSIGNED:
+ if ((record->flags & MEM_UInt) == MEM_UInt)
+ return 0;
+ if ((record->flags & MEM_Int) == MEM_Int){
+ MemSetTypeFlag(record, MEM_UInt);
+ return 0;
+ }
+ if ((record->flags & MEM_Real) == MEM_Real) {
+ uint64_t i = (uint64_t) record->u.r;
+ if (i == record->u.r) {
+ record->u.u = i;
+ MemSetTypeFlag(record, MEM_UInt);
+ }
+ return 0;
+ }
+ return sqlVdbeMemIntegerify(record, false);
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:
@@ -358,10 +381,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;
@@ -404,15 +427,18 @@ sql_value_apply_type(
* numeric type, if has one. Set the pMem->u.r and pMem->u.i fields
* accordingly.
*/
-static u16 SQL_NOINLINE computeNumericType(Mem *pMem)
+static u32 SQL_NOINLINE computeNumericType(Mem *pMem)
{
- assert((pMem->flags & (MEM_Int|MEM_Real))==0);
+ assert((pMem->flags & (MEM_Int|MEM_Real|MEM_UInt))==0);
assert((pMem->flags & (MEM_Str|MEM_Blob))!=0);
if (sqlAtoF(pMem->z, &pMem->u.r, pMem->n)==0)
return 0;
- if (sql_atoi64(pMem->z, (int64_t *)&pMem->u.i, pMem->n)==SQL_OK)
- return MEM_Int;
- 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_UInt
+ : MEM_Int;
}
/*
@@ -422,10 +448,10 @@ static u16 SQL_NOINLINE computeNumericType(Mem *pMem)
* Unlike mem_apply_numeric_type(), this routine does not modify pMem->flags.
* But it does set pMem->u.r and pMem->u.i appropriately.
*/
-static u16 numericType(Mem *pMem)
+static u32 numericType(Mem *pMem)
{
- if (pMem->flags & (MEM_Int|MEM_Real)) {
- return pMem->flags & (MEM_Int|MEM_Real);
+ if (pMem->flags & (MEM_Int|MEM_Real|MEM_UInt)) {
+ return pMem->flags & (MEM_Int|MEM_Real|MEM_UInt);
}
if (pMem->flags & (MEM_Str|MEM_Blob)) {
return computeNumericType(pMem);
@@ -530,6 +556,10 @@ 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|MEM_Str))==(MEM_UInt|MEM_Str)) {
+ printf(" su:%llu", p->u.u);
+ } else if (p->flags & MEM_UInt) {
+ printf(" u:%llu", p->u.u);
#ifndef SQL_OMIT_FLOATING_POINT
} else if (p->flags & MEM_Real) {
printf(" r:%g", p->u.r);
@@ -638,6 +668,8 @@ mem_type_to_str(const struct Mem *p)
return "BLOB";
case MEM_Bool:
return "BOOLEAN";
+ case MEM_UInt:
+ return "UNSIGNED";
default:
unreachable();
}
@@ -1141,6 +1173,8 @@ case OP_Int64: { /* out2 */
pOut = out2Prerelease(p, pOp);
assert(pOp->p4.pI64!=0);
pOut->u.i = *pOp->p4.pI64;
+ if (pOp->p4type == P4_UINT64)
+ pOut->flags = MEM_UInt;
break;
}
@@ -1435,9 +1469,12 @@ 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);
+ if (pIn1->flags & MEM_UInt)
+ sqlVdbeMemSetUInt64(pOut, pIn1->u.u);
+ else
+ sqlVdbeMemSetInt64(pOut, pIn1->u.i);
break;
}
@@ -1646,8 +1683,8 @@ case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */
case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
char bIntint; /* Started out as two integer operands */
u32 flags; /* Combined MEM_* flags from both inputs */
- u16 type1; /* Numeric type of left operand */
- u16 type2; /* Numeric type of right operand */
+ u32 type1; /* Numeric type of left operand */
+ u32 type2; /* Numeric type of right operand */
i64 iA; /* Integer value of left operand */
i64 iB; /* Integer value of right operand */
double rA; /* Real value of left operand */
@@ -1660,31 +1697,32 @@ 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)) && (type2 & (MEM_Int | MEM_UInt))) {
iA = pIn1->u.i;
iB = pIn2->u.i;
+ bool is_signedA = (type1 & MEM_UInt) == 0;
+ bool is_signedB = (type2 & MEM_UInt) == 0;
bIntint = 1;
+ enum arithmetic_result arr;
switch( pOp->opcode) {
- case OP_Add: if (sqlAddInt64(&iB,iA)) goto integer_overflow; break;
- case OP_Subtract: if (sqlSubInt64(&iB,iA)) goto integer_overflow; break;
- case OP_Multiply: if (sqlMulInt64(&iB,iA)) goto integer_overflow; break;
- case OP_Divide: {
- if (iA == 0)
- goto division_by_zero;
- if (iA==-1 && iB==SMALLEST_INT64) goto integer_overflow;
- iB /= iA;
- break;
+ case OP_Add: arr = sqlAddInt64(&iB, is_signedB, iA, is_signedA); break;
+ case OP_Subtract: arr = sqlSubInt64(&iB, is_signedB, iA, is_signedA); break;
+ case OP_Multiply: arr = sqlMulInt64(&iB, is_signedB, iA, is_signedA); break;
+ case OP_Divide: arr = sqlDivInt64(&iB, is_signedB, iA, is_signedA); break;
+ default: arr = sqlRemInt64(&iB, is_signedB, iA, is_signedA); break;
}
- default: {
- if (iA == 0)
- goto division_by_zero;
- if (iA==-1) iA = 1;
- iB %= iA;
+
+ switch(arr){
+ case ATHR_SIGNED:
+ MemSetTypeFlag(pOut, MEM_Int);
break;
- }
+ case ATHR_UNSIGNED:
+ MemSetTypeFlag(pOut, MEM_UInt);
+ break;
+ case ATHR_OVERFLOW: goto integer_overflow;
+ case ATHR_DIVBYZERO: goto division_by_zero;
}
pOut->u.i = iB;
- MemSetTypeFlag(pOut, MEM_Int);
} else {
bIntint = 0;
if (sqlVdbeRealValue(pIn1, &rA) != 0) {
@@ -1745,7 +1783,7 @@ division_by_zero:
rc = SQL_TARANTOOL_ERROR;
goto abort_due_to_error;
integer_overflow:
- diag_set(ClientError, ER_SQL_EXECUTE, "integer is overflowed");
+ diag_set(ClientError, ER_SQL_EXECUTE, "integer overflow");
rc = SQL_TARANTOOL_ERROR;
goto abort_due_to_error;
}
@@ -1931,13 +1969,15 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
sqlVdbeMemSetNull(pOut);
break;
}
- if (sqlVdbeIntValue(pIn2, (int64_t *) &iA) != 0) {
+ bool is_unsignedA = false;
+ bool is_unsignedB = false;
+ if (sqlVdbeIntValue(pIn2, (int64_t *) &iA, &is_unsignedA) != 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, (int64_t *) &iB, &is_unsignedB) != 0) {
diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
sql_value_text(pIn1), "integer");
rc = SQL_TARANTOOL_ERROR;
@@ -1951,6 +1991,10 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
} else if (iB!=0) {
assert(op==OP_ShiftRight || op==OP_ShiftLeft);
+ if (is_unsignedB){
+ /* Limit big unsigned values by 64 */
+ iB = 64;
+ }
/* If shifting by a negative amount, shift in the other direction */
if (iB<0) {
assert(OP_ShiftRight==OP_ShiftLeft+1);
@@ -2002,6 +2046,9 @@ case OP_AddImm: { /* in1 */
*/
case OP_MustBeInt: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
+ if ((pIn1->flags & MEM_UInt)!=0)
+ break;
+
if ((pIn1->flags & MEM_Int)==0) {
mem_apply_type(pIn1, FIELD_TYPE_INTEGER);
VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2);
@@ -2030,7 +2077,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)) {
sqlVdbeMemRealify(pIn1);
}
break;
@@ -2207,12 +2254,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, 0);
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) != 0) {
diag_set(ClientError,
ER_SQL_TYPE_MISMATCH,
@@ -2233,17 +2280,25 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
res = 0;
goto compare_op;
}
+ if ((pIn1->flags & pIn3->flags & MEM_UInt)!=0) {
+ if (pIn3->u.u > pIn1->u.u) { res = +1; goto compare_op; }
+ if (pIn3->u.u < pIn1->u.u) { 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_Real|MEM_UInt))!=0) {
testcase( pIn1->flags & MEM_Int);
+ testcase( pIn1->flags & MEM_UInt);
testcase( pIn1->flags & MEM_Real);
sqlVdbeMemStringify(pIn1, 1);
testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn));
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_Real|MEM_UInt))!=0) {
testcase( pIn3->flags & MEM_Int);
+ testcase( pIn3->flags & MEM_UInt);
testcase( pIn3->flags & MEM_Real);
sqlVdbeMemStringify(pIn3, 1);
testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn));
@@ -2458,7 +2513,8 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */
v1 = 2;
} else {
int64_t i;
- if (sqlVdbeIntValue(pIn1, &i) != 0) {
+ bool is_unsigned = false;
+ if (sqlVdbeIntValue(pIn1, &i, &is_unsigned) != 0) {
diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
sql_value_text(pIn1), "integer");
rc = SQL_TARANTOOL_ERROR;
@@ -2471,7 +2527,8 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */
v2 = 2;
} else {
int64_t i;
- if (sqlVdbeIntValue(pIn2, &i) != 0) {
+ bool is_unsigned = false;
+ if (sqlVdbeIntValue(pIn2, &i, &is_unsigned) != 0) {
diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
sql_value_text(pIn2), "integer");
rc = SQL_TARANTOOL_ERROR;
@@ -2509,7 +2566,8 @@ case OP_Not: { /* same as TK_NOT, in1, out2 */
sqlVdbeMemSetNull(pOut);
if ((pIn1->flags & MEM_Null)==0) {
int64_t i;
- if (sqlVdbeIntValue(pIn1, &i) != 0) {
+ bool is_unsigned = false;
+ if (sqlVdbeIntValue(pIn1, &i, &is_unsigned) != 0) {
diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
sql_value_text(pIn1), "integer");
rc = SQL_TARANTOOL_ERROR;
@@ -2534,7 +2592,8 @@ 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_unsigned = false;
+ if (sqlVdbeIntValue(pIn1, &i, &is_unsigned) != 0) {
diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
sql_value_text(pIn1), "integer");
rc = SQL_TARANTOOL_ERROR;
@@ -3533,6 +3592,7 @@ case OP_SeekGT: { /* jump, in3 */
UnpackedRecord r; /* The key to seek for */
int nField; /* Number of columns or fields in the key */
i64 iKey; /* The id we are to seek to */
+ u32 key_type = MEM_Int; /* Type of the iKey, integer by default */
int eqOnly; /* Only interested in == results */
int reg_ipk=0; /* Register number which holds IPK. */
@@ -3561,12 +3621,14 @@ 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_Real|MEM_Str|MEM_UInt))==MEM_Str) {
mem_apply_numeric_type(pIn3, 0);
+ key_type = pIn3->flags & MEM_PURE_TYPE_MASK;
}
int64_t i;
- if ((pIn3->flags & MEM_Int) == MEM_Int) {
+ if ((pIn3->flags & (MEM_Int | MEM_UInt)) != 0) {
i = pIn3->u.i;
+ key_type = pIn3->flags & MEM_PURE_TYPE_MASK;
} else if ((pIn3->flags & MEM_Real) == MEM_Real) {
if (pIn3->u.r > INT64_MAX)
i = INT64_MAX;
@@ -3585,7 +3647,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
@@ -3643,7 +3705,7 @@ case OP_SeekGT: { /* jump, in3 */
if (reg_ipk > 0) {
aMem[reg_ipk].u.i = iKey;
- aMem[reg_ipk].flags = MEM_Int;
+ aMem[reg_ipk].flags = key_type;
}
r.default_rc = ((1 & (oc - OP_SeekLT)) ? -1 : +1);
@@ -5168,7 +5230,8 @@ case OP_OffsetLimit: { /* in1, out2, in3 */
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)) {
+ if (x<=0 ||
+ sqlAddInt64(&x, true, pIn3->u.i>0?pIn3->u.i:0, true) != ATHR_SIGNED) {
/* 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
diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
index f9bb96f09..d4383901c 100644
--- a/src/box/sql/vdbe.h
+++ b/src/box/sql/vdbe.h
@@ -70,7 +70,7 @@ struct VdbeOp {
int i; /* Integer value if p4type==P4_INT32 */
void *p; /* Generic pointer */
char *z; /* Pointer to data for string (char array) types */
- i64 *pI64; /* Used when p4type is P4_INT64 */
+ i64 *pI64; /* Used when p4type is P4_INT64 or P4_UINT64 */
double *pReal; /* Used when p4type is P4_REAL */
FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */
sql_context *pCtx; /* Used when p4type is P4_FUNCCTX */
@@ -131,6 +131,7 @@ struct SubProgram {
#define P4_INTARRAY (-12) /* P4 is a vector of 32-bit integers */
#define P4_SUBPROGRAM (-13) /* P4 is a pointer to a SubProgram structure */
#define P4_ADVANCE (-14) /* P4 is a pointer to BtreeNext() or BtreePrev() */
+#define P4_UINT64 (-15) /* P4 is a 64-bit unsigned integer */
#define P4_FUNCCTX (-16) /* P4 is a pointer to an sql_context object */
#define P4_BOOL (-17) /* P4 is a bool value */
#define P4_PTR (-18) /* P4 is a generic pointer */
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index 61b7d58b2..5b2d129f7 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 */
@@ -234,7 +235,7 @@ struct Mem {
#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 */
-#define MEM_TypeMask 0x83ff /* Mask of type bits */
+#define MEM_TypeMask 0x283ff /* Mask of type bits */
/* Whenever Mem contains a valid string or blob representation, one of
* the following flags must be set to determine the memory management
@@ -248,6 +249,8 @@ 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_UInt 0x20000 /* Value is unsigned integer. */
+
#ifdef SQL_OMIT_INCRBLOB
#undef MEM_Zero
#define MEM_Zero 0x0000
@@ -259,7 +262,9 @@ struct Mem {
* auxiliary flags.
*/
enum {
- MEM_PURE_TYPE_MASK = 0x1f
+ MEM_PURE_TYPE_MASK = MEM_Null | MEM_Str | MEM_Int |
+ MEM_Real | MEM_Blob | MEM_Bool |
+ MEM_UInt
};
@@ -273,7 +278,7 @@ enum {
* Clear any existing type flags from a Mem and replace them with f
*/
#define MemSetTypeFlag(p, f) \
- ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f)
+ ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero|MEM_UInt))|f)
/*
* Return true if a memory cell is not marked as invalid. This macro
@@ -475,6 +480,7 @@ void sqlVdbeMemMove(Mem *, Mem *);
int sqlVdbeMemNulTerminate(Mem *);
int sqlVdbeMemSetStr(Mem *, const char *, int, u8, void (*)(void *));
void sqlVdbeMemSetInt64(Mem *, i64);
+void sqlVdbeMemSetUInt64(Mem *, u64);
#ifdef SQL_OMIT_FLOATING_POINT
#define sqlVdbeMemSetDouble sqlVdbeMemSetInt64
#else
@@ -485,7 +491,7 @@ 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 *);
int sqlVdbeMemIntegerify(Mem *, bool is_forced);
int sqlVdbeRealValue(Mem *, double *);
int mem_apply_integer_type(Mem *);
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index d7e89073e..e138c5abd 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -215,7 +215,8 @@ int
sql_value_int(sql_value * pVal)
{
int64_t i;
- sqlVdbeIntValue((Mem *) pVal, &i);
+ bool is_unsigned = false;
+ sqlVdbeIntValue((Mem *) pVal, &i, &is_unsigned);
return (int)i;
}
@@ -223,7 +224,17 @@ sql_int64
sql_value_int64(sql_value * pVal)
{
int64_t i;
- sqlVdbeIntValue((Mem *) pVal, &i);
+ bool is_unsigned = false;
+ sqlVdbeIntValue((Mem *) pVal, &i, &is_unsigned);
+ 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;
}
@@ -246,41 +257,38 @@ sql_value_text(sql_value * pVal)
int
sql_value_type(sql_value * pVal)
{
- static const u8 aType[] = {
- SQL_BLOB, /* 0x00 */
- SQL_NULL, /* 0x01 */
- SQL_TEXT, /* 0x02 */
- SQL_NULL, /* 0x03 */
- SQL_INTEGER, /* 0x04 */
- SQL_NULL, /* 0x05 */
- SQL_INTEGER, /* 0x06 */
- SQL_NULL, /* 0x07 */
- SQL_FLOAT, /* 0x08 */
- SQL_NULL, /* 0x09 */
- SQL_FLOAT, /* 0x0a */
- SQL_NULL, /* 0x0b */
- SQL_INTEGER, /* 0x0c */
- SQL_NULL, /* 0x0d */
- SQL_INTEGER, /* 0x0e */
- SQL_NULL, /* 0x0f */
- SQL_BLOB, /* 0x10 */
- SQL_NULL, /* 0x11 */
- SQL_TEXT, /* 0x12 */
- SQL_NULL, /* 0x13 */
- SQL_INTEGER, /* 0x14 */
- SQL_NULL, /* 0x15 */
- SQL_INTEGER, /* 0x16 */
- SQL_NULL, /* 0x17 */
- SQL_FLOAT, /* 0x18 */
- SQL_NULL, /* 0x19 */
- SQL_FLOAT, /* 0x1a */
- SQL_NULL, /* 0x1b */
- SQL_INTEGER, /* 0x1c */
- SQL_NULL, /* 0x1d */
- SQL_INTEGER, /* 0x1e */
- SQL_NULL, /* 0x1f */
- };
- return aType[pVal->flags & MEM_PURE_TYPE_MASK];
+ /*
+ * The order of the comparisons is essential.
+ * Once the value change its type,
+ * e.g. string or blob to integer/real
+ * the dynamic data are not disposed,
+ * they are kept together with scalar value.
+ * Thus the field 'flags' accumulates several
+ * bits responsible for data type.
+ * So the right order is following:
+ * - Null
+ * - Unsigned integer
+ * - Signed integer
+ * - Real
+ * - Bool
+ * - String
+ * - Blob
+ */
+ if ((pVal->flags & MEM_Null) != 0)
+ return SQL_NULL;
+ if ((pVal->flags & MEM_UInt) != 0)
+ return SQL_UNSIGNED;
+ if ((pVal->flags & MEM_Int) != 0)
+ return SQL_INTEGER;
+ if ((pVal->flags & MEM_Real) != 0)
+ return SQL_FLOAT;
+ if ((pVal->flags & MEM_Bool) != 0)
+ return SQL_INTEGER;
+ if ((pVal->flags & MEM_Str) != 0)
+ return SQL_TEXT;
+ if ((pVal->flags & MEM_Blob) != 0)
+ return SQL_BLOB;
+ return SQL_NULL; /* Unknown type */
}
/* Make a copy of an sql_value object
@@ -410,6 +418,15 @@ sql_result_int64(sql_context * pCtx, i64 iVal)
sqlVdbeMemSetInt64(pCtx->pOut, iVal);
}
+void
+sql_result_uint64(sql_context * pCtx, u64 iVal)
+{
+ if (iVal > INT64_MAX)
+ sqlVdbeMemSetUInt64(pCtx->pOut, iVal);
+ else
+ sqlVdbeMemSetInt64(pCtx->pOut, (i64)iVal);
+}
+
void
sql_result_null(sql_context * pCtx)
{
@@ -1016,6 +1033,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)
{
@@ -1367,6 +1392,19 @@ sql_bind_int64(sql_stmt * pStmt, int i, sql_int64 iValue)
return rc;
}
+int
+sql_bind_uint64(sql_stmt * pStmt, int i, sql_uint64 iValue)
+{
+ int rc;
+ Vdbe *p = (Vdbe *) pStmt;
+ rc = vdbeUnbind(p, i);
+ if (rc == SQL_OK) {
+ rc = sql_bind_type(p, i, "UNSIGNED");
+ sqlVdbeMemSetUInt64(&p->aVar[i - 1], iValue);
+ }
+ return rc;
+}
+
int
sql_bind_null(sql_stmt * pStmt, int i)
{
@@ -1410,6 +1448,10 @@ sql_bind_value(sql_stmt * pStmt, int i, const sql_value * pValue)
rc = sql_bind_int64(pStmt, i, pValue->u.i);
break;
}
+ case SQL_UNSIGNED: {
+ rc = sql_bind_uint64(pStmt, i, pValue->u.i);
+ break;
+ }
case SQL_FLOAT:{
rc = sql_bind_double(pStmt, i, pValue->u.r);
break;
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 0cc3c1487..3fbce2f01 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -862,6 +862,7 @@ freeP4(sql * db, int p4type, void *p4)
}
case P4_REAL:
case P4_INT64:
+ case P4_UINT64:
case P4_DYNAMIC:
case P4_INTARRAY:{
sqlDbFree(db, p4);
@@ -1354,6 +1355,10 @@ displayP4(Op * pOp, char *zTemp, int nTemp)
sqlXPrintf(&x, "%lld", *pOp->p4.pI64);
break;
}
+ case P4_UINT64:{
+ sqlXPrintf(&x, "%ull", *pOp->p4.pI64);
+ break;
+ }
case P4_INT32:{
sqlXPrintf(&x, "%d", pOp->p4.i);
break;
@@ -1368,6 +1373,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) {
@@ -2851,7 +2858,7 @@ sqlVdbeSerialType(Mem * pMem, int file_format, u32 * pLen)
*pLen = 0;
return 0;
}
- if (flags & MEM_Int) {
+ if (flags & (MEM_Int|MEM_UInt)) {
/* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */
#define MAX_6BYTE ((((i64)0x00008000)<<32)-1)
i64 i = pMem->u.i;
@@ -3357,6 +3364,44 @@ sqlIntFloatCompare(i64 i, double r)
}
}
+/*
+ * Do a comparison between a 64-bit signed integer and a 64-bit
+ * unsigned integer.
+ * Return negative, zero, or positive if the first (i64) is less than,
+ * equal to, or greater than the second (u64).
+ */
+static int
+sqlIntUIntCompare(i64 i, u64 u)
+{
+ if (i < 0)
+ return -1;
+ if ((u64)i < u)
+ return -1;
+ else if ((u64)i > u)
+ return +1;
+ else
+ return 0;
+}
+
+/*
+ * Do a comparison between a 64-bit unsigned integer and a 64-bit
+ * floating-point number.
+ * Return negative, zero, or positive if the first (u64)
+ * is less than, equal to, or greater than the second (double).
+ */
+static int
+sqlUIntFloatCompare(u64 u, double r)
+{
+ if (r < 0.0)
+ return +1;
+ double s = (double)u;
+ if (s < r)
+ return -1;
+ if (s > r)
+ return +1;
+ return 0;
+}
+
/*
* Compare the values contained by the two memory cells, returning
* negative, zero or positive if pMem1 is less than, equal to, or greater
@@ -3385,7 +3430,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_Real | MEM_UInt)) {
if ((f1 & f2 & MEM_Int) != 0) {
if (pMem1->u.i < pMem2->u.i)
return -1;
@@ -3393,6 +3438,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;
@@ -3404,6 +3456,20 @@ sqlMemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pColl)
if ((f2 & MEM_Real) != 0) {
return sqlIntFloatCompare(pMem1->u.i,
pMem2->u.r);
+ } else if ((f2 & MEM_UInt) != 0){
+ return sqlIntUIntCompare(pMem1->u.i,
+ pMem2->u.u);
+ } else {
+ return -1;
+ }
+ }
+ if ((f1 & MEM_UInt) != 0) {
+ if ((f2 & MEM_Real) != 0) {
+ return sqlUIntFloatCompare(pMem1->u.u,
+ pMem2->u.r);
+ } else if ((f2 & MEM_Int) != 0){
+ return -sqlIntUIntCompare(pMem2->u.i,
+ pMem1->u.u);
} else {
return -1;
}
@@ -3411,7 +3477,10 @@ sqlMemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pColl)
if ((f1 & MEM_Real) != 0) {
if ((f2 & MEM_Int) != 0) {
return -sqlIntFloatCompare(pMem2->u.i,
- pMem1->u.r);
+ pMem1->u.r);
+ } else if ((f2 & MEM_UInt) != 0) {
+ return -sqlUIntFloatCompare(pMem2->u.u,
+ pMem1->u.r);
} else {
return -1;
}
@@ -3567,13 +3636,24 @@ sqlVdbeCompareMsgpack(const char **key1,
goto do_int;
}
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) {
+ rc = -sqlIntUIntCompare(pKey2->u.i,
+ mem1.u.u);
+ } else if (pKey2->flags & MEM_Real) {
+ rc = sqlUIntFloatCompare(mem1.u.u,
+ pKey2->u.r);
+ } else if (pKey2->flags & MEM_UInt) {
+ if (mem1.u.u < pKey2->u.u) {
+ rc = -1;
+ } else if (mem1.u.u > pKey2->u.u) {
+ rc = +1;
+ }
+ } 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);
@@ -3587,6 +3667,9 @@ sqlVdbeCompareMsgpack(const char **key1,
} else if (pKey2->flags & MEM_Real) {
rc = sqlIntFloatCompare(mem1.u.i,
pKey2->u.r);
+ } else if (pKey2->flags & MEM_UInt) {
+ rc = sqlIntUIntCompare(mem1.u.i,
+ pKey2->u.u);
} else {
rc = (pKey2->flags & MEM_Null) ? +1 : -1;
}
@@ -3724,13 +3807,10 @@ vdbe_decode_msgpack_into_mem(const char *buf, struct Mem *mem, uint32_t *len)
}
case MP_UINT: {
uint64_t v = mp_decode_uint(&buf);
- if (v > INT64_MAX) {
- diag_set(ClientError, ER_SQL_EXECUTE,
- "integer is overflowed");
- return -1;
- }
mem->u.i = v;
mem->flags = MEM_Int;
+ if (v > INT64_MAX)
+ mem->flags = MEM_UInt;
break;
}
case MP_INT: {
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 074ff8c96..a30b131a6 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -66,7 +66,8 @@ sqlVdbeCheckMemInvariants(Mem * p)
assert((p->flags & MEM_Dyn) == 0 || p->szMalloc == 0);
/* Cannot be both MEM_Int and MEM_Real at the same time */
- assert((p->flags & (MEM_Int | MEM_Real)) != (MEM_Int | MEM_Real));
+ assert((p->flags & (MEM_Int | MEM_Real | MEM_UInt)) !=
+ (MEM_Int | MEM_Real| MEM_UInt));
/* The szMalloc field holds the correct memory allocation size */
assert(p->szMalloc == 0
@@ -173,7 +174,7 @@ sqlVdbeMemClearAndResize(Mem * pMem, int szNew)
}
assert((pMem->flags & MEM_Dyn) == 0);
pMem->z = pMem->zMalloc;
- pMem->flags &= (MEM_Null | MEM_Int | MEM_Real);
+ pMem->flags &= (MEM_Null | MEM_Int | MEM_Real | MEM_UInt);
return SQL_OK;
}
@@ -289,7 +290,7 @@ sqlVdbeMemStringify(Mem * pMem, u8 bForce)
return SQL_OK;
assert(!(fg & MEM_Zero));
- assert(fg & (MEM_Int | MEM_Real));
+ assert(fg & (MEM_Int | MEM_Real | MEM_UInt));
assert(EIGHT_BYTE_ALIGNMENT(pMem));
if (sqlVdbeMemClearAndResize(pMem, nByte)) {
@@ -297,6 +298,8 @@ sqlVdbeMemStringify(Mem * pMem, u8 bForce)
}
if (fg & MEM_Int) {
sql_snprintf(nByte, pMem->z, "%lld", pMem->u.i);
+ } else if (fg & MEM_UInt) {
+ sql_snprintf(nByte, pMem->z, "%llu", pMem->u.u);
} else {
assert(fg & MEM_Real);
sql_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
@@ -304,7 +307,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_Real | MEM_UInt);
return SQL_OK;
}
@@ -411,39 +414,36 @@ sqlVdbeMemRelease(Mem * p)
}
/*
- * Convert a 64-bit IEEE double into a 64-bit signed integer.
- * If the double is out of range of a 64-bit signed integer then
- * return the closest available 64-bit signed integer.
+ * Convert a 64-bit IEEE double into a 64-bit signed or unsigned integer.
+ * If the double is out of range of a 64-bit unsigned integer then
+ * return the closest available 64-bit unsigned integer.
+ * Returns 0 on success, -1 on error and 1 on precision loss.
*/
static int
-doubleToInt64(double r, int64_t *i)
+doubleToInt64(double r, int64_t *i, bool* is_unsigned)
{
-#ifdef SQL_OMIT_FLOATING_POINT
- /* When floating-point is omitted, double and int64 are the same thing */
- *i = r;
- return 0;
-#else
- /*
- * Many compilers we encounter do not define constants for the
- * minimum and maximum 64-bit integers, or they define them
- * inconsistently. And many do not understand the "LL" notation.
- * So we define our own static constants here using nothing
- * larger than a 32-bit integer constant.
- */
- static const int64_t maxInt = LARGEST_INT64;
- static const int64_t minInt = SMALLEST_INT64;
+ static const int64_t minInt = INT64_MIN;
+ static const uint64_t maxUInt = UINT64_MAX;
+ static const int64_t maxInt = INT64_MAX;
- if (r <= (double)minInt) {
+ if (r < (double)minInt){
*i = minInt;
+ *is_unsigned = false;
return -1;
- } else if (r >= (double)maxInt) {
- *i = maxInt;
+ } else if (r > (double)maxUInt) {
+ *i = maxUInt;
+ *is_unsigned = true;
return -1;
+ } else if (r >= (double)maxInt){
+ uint64_t t = (uint64_t) r;
+ *i = (int64_t) t;
+ *is_unsigned = true;
+ return (t != r) ? 1 : 0;
} else {
*i = (int64_t) r;
- return *i != r;
+ *is_unsigned = false;
+ return (*i != r) ? 1 : 0;
}
-#endif
}
/*
@@ -458,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_unsigned)
{
int flags;
assert(EIGHT_BYTE_ALIGNMENT(pMem));
flags = pMem->flags;
if (flags & MEM_Int) {
*i = pMem->u.i;
+ *is_unsigned = false;
+ return 0;
+ } else if (flags & MEM_UInt) {
+ *i = (i64)pMem->u.u;
+ *is_unsigned = true;
return 0;
} else if (flags & MEM_Real) {
- return doubleToInt64(pMem->u.r, i);
+ return doubleToInt64(pMem->u.r, i, is_unsigned);
} else if (flags & (MEM_Str)) {
assert(pMem->z || pMem->n == 0);
- if (sql_atoi64(pMem->z, (int64_t *)i, pMem->n) == 0)
- return 0;
+ return sql_atoi64(pMem->z, i, is_unsigned, pMem->n);
}
return -1;
}
@@ -489,6 +493,9 @@ sqlVdbeRealValue(Mem * pMem, double *v)
if (pMem->flags & MEM_Real) {
*v = pMem->u.r;
return 0;
+ } else if (pMem->flags & MEM_UInt) {
+ *v = (double)pMem->u.u;
+ return 0;
} else if (pMem->flags & MEM_Int) {
*v = (double)pMem->u.i;
return 0;
@@ -511,9 +518,14 @@ 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) {
+ bool is_unsigned = false;
+
+ if ((rc = doubleToInt64(pMem->u.r, (int64_t *) &ix, &is_unsigned)) == 0) {
pMem->u.i = ix;
- MemSetTypeFlag(pMem, MEM_Int);
+ if (is_unsigned)
+ MemSetTypeFlag(pMem, MEM_UInt);
+ else
+ MemSetTypeFlag(pMem, MEM_Int);
}
return rc;
}
@@ -527,15 +539,23 @@ sqlVdbeMemIntegerify(Mem * pMem, bool is_forced)
assert(EIGHT_BYTE_ALIGNMENT(pMem));
int64_t i;
- if (sqlVdbeIntValue(pMem, &i) == 0) {
+ bool is_unsigned = false;
+ if (sqlVdbeIntValue(pMem, &i, &is_unsigned) == 0) {
pMem->u.i = i;
- MemSetTypeFlag(pMem, MEM_Int);
+
+ if (is_unsigned)
+ MemSetTypeFlag(pMem, MEM_UInt);
+ else
+ MemSetTypeFlag(pMem, MEM_Int);
return 0;
} else if ((pMem->flags & MEM_Real) != 0 && is_forced) {
- if (pMem->u.r >= INT64_MAX || pMem->u.r < INT64_MIN)
+ if (doubleToInt64(pMem->u.r, (int64_t*)&pMem->u.i, &is_unsigned) < 0)
return -1;
- pMem->u.i = (int64_t) pMem->u.r;
- MemSetTypeFlag(pMem, MEM_Int);
+
+ if (is_unsigned)
+ MemSetTypeFlag(pMem, MEM_UInt);
+ else
+ MemSetTypeFlag(pMem, MEM_Int);
return 0;
}
@@ -576,10 +596,17 @@ sqlVdbeMemRealify(Mem * pMem)
int
sqlVdbeMemNumerify(Mem * pMem)
{
- if ((pMem->flags & (MEM_Int | MEM_Real | MEM_Null)) == 0) {
+ if ((pMem->flags & (MEM_Int | MEM_Real | MEM_Null | MEM_UInt)) == 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_UInt);
+ else
+ MemSetTypeFlag(pMem, MEM_Int);
} else {
double v;
if (sqlVdbeRealValue(pMem, &v))
@@ -589,7 +616,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_Real | MEM_Null | MEM_UInt)) != 0);
pMem->flags &= ~(MEM_Str | MEM_Blob | MEM_Zero);
return SQL_OK;
}
@@ -608,9 +635,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);
@@ -620,10 +653,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_UInt);
+ else
+ MemSetTypeFlag(pMem, MEM_Int);
return 0;
}
return sqlVdbeMemIntegerify(pMem, true);
@@ -635,7 +673,7 @@ 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_Real | MEM_Blob | MEM_Zero | MEM_UInt);
return SQL_OK;
}
}
@@ -711,6 +749,14 @@ vdbeReleaseAndSetInt64(Mem * pMem, i64 val)
pMem->flags = MEM_Int;
}
+static SQL_NOINLINE void
+vdbeReleaseAndSetUInt64(Mem * pMem, u64 val)
+{
+ sqlVdbeMemSetNull(pMem);
+ pMem->u.u = val;
+ pMem->flags = MEM_UInt;
+}
+
/*
* Delete any previous value and set the value stored in *pMem to val,
* manifest type INTEGER.
@@ -726,6 +772,17 @@ sqlVdbeMemSetInt64(Mem * pMem, i64 val)
}
}
+void
+sqlVdbeMemSetUInt64(Mem * pMem, u64 val)
+{
+ if (VdbeMemDynamic(pMem)) {
+ vdbeReleaseAndSetUInt64(pMem, val);
+ } else {
+ pMem->u.u = val;
+ pMem->flags = MEM_UInt;
+ }
+}
+
#ifndef SQL_OMIT_FLOATING_POINT
/*
* Delete any previous value and set the value stored in *pMem to val,
@@ -1312,7 +1369,7 @@ valueFromExpr(sql * db, /* The database connection */
} else {
sql_value_apply_type(pVal, type);
}
- if (pVal->flags & (MEM_Int | MEM_Real))
+ if (pVal->flags & (MEM_Int | MEM_Real | MEM_UInt))
pVal->flags &= ~MEM_Str;
} else if (op == TK_UMINUS) {
/* This branch happens for multiple negative signs. Ex: -(-5) */
@@ -1727,6 +1784,8 @@ encode_int:
mpstream_encode_uint(stream, i);
else
mpstream_encode_int(stream, i);
+ } else if (var->flags & MEM_UInt) {
+ mpstream_encode_uint(stream, var->u.u);
} else if (var->flags & MEM_Str) {
mpstream_encode_strn(stream, var->z, var->n);
} else if (var->flags & MEM_Bool) {
diff --git a/src/box/sql/vdbetrace.c b/src/box/sql/vdbetrace.c
index cb930538d..361e0841a 100644
--- a/src/box/sql/vdbetrace.c
+++ b/src/box/sql/vdbetrace.c
@@ -158,6 +158,8 @@ sqlVdbeExpandSql(Vdbe * p, /* The prepared statement being evaluated */
sqlStrAccumAppend(&out, "NULL", 4);
} else if (pVar->flags & MEM_Int) {
sqlXPrintf(&out, "%lld", pVar->u.i);
+ } else if (pVar->flags & MEM_UInt) {
+ sqlXPrintf(&out, "%llu", pVar->u.u);
} else if (pVar->flags & MEM_Real) {
sqlXPrintf(&out, "%!.15g", pVar->u.r);
} else if (pVar->flags & MEM_Str) {
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 889fc5867..1f328edb4 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env tarantool
test = require("sqltester")
-test:plan(14586)
+test:plan(14587)
--!./tcltestrunner.lua
-- 2001 September 15
@@ -919,7 +919,7 @@ test:do_execsql_test(
UNION ALL SELECT -9223372036854775807)
]], {
-- <func-8.7>
- "real"
+ "integer"
-- </func-8.7>
})
@@ -1591,17 +1591,18 @@ test:do_execsql_test(
-- </func-18.11>
})
-test:do_catchsql_test(
+test:do_execsql_test(
"func-18.12",
[[
INSERT INTO t6 VALUES(3, 1<<62);
SELECT sum(x) - ((1<<62)*2.0+1) from t6;
]], {
-- <func-18.12>
- 1, "integer overflow"
+ 0
-- </func-18.12>
})
+
test:do_execsql_test(
"func-18.13",
[[
@@ -1612,6 +1613,17 @@ test:do_execsql_test(
-- </func-18.13>
})
+test:do_execsql_test(
+ "func-18.12a",
+ [[
+ INSERT INTO t6 VALUES(4, 13+ (1<<62));
+ SELECT avg(x) from t6;
+ ]], {
+ -- <func-18.12>
+ 3458764513820540928
+ -- </func-18.12>
+ })
+
test:do_execsql_test(
"func-18.14",
[[
@@ -1653,7 +1665,7 @@ test:do_catchsql_test(
SELECT 10 AS x);
]], {
-- <func-18.15>
- 1, "integer overflow"
+ 0, {9223372036854775817ULL}
-- </func-18.15>
})
diff --git a/test/sql-tap/hexlit.test.lua b/test/sql-tap/hexlit.test.lua
index 158eda73b..1597d4b8a 100755
--- a/test/sql-tap/hexlit.test.lua
+++ b/test/sql-tap/hexlit.test.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env tarantool
test = require("sqltester")
-test:plan(128)
+test:plan(130)
--!./tcltestrunner.lua
-- 2014-07-23
@@ -91,7 +91,9 @@ hexlit1(160, "0X1000000000000000", 1152921504606846976LL)
hexlit1(161, "0x2000000000000000", 2305843009213693952LL)
hexlit1(162, "0X4000000000000000", 4611686018427387904LL)
hexlit1(163, "0x8000000000000000", -9223372036854775808LL)
-hexlit1(164, "0XFFFFFFFFFFFFFFFF", -1)
+hexlit1(164, "0x8000000000000000", 9223372036854775808ULL)
+hexlit1(165, "0x8000000000000001", 9223372036854775809ULL)
+hexlit1(166, "0XFFFFFFFFFFFFFFFF", 18446744073709551615ULL)
for n = 1, 0x10 -1, 1 do
hexlit1("200."..n..".1", "0X"..string.format("%03X",n), n)
hexlit1("200."..n..".2", "0x"..string.format("%03X",n), n)
diff --git a/test/sql/gh-2347-max-int-literals.result b/test/sql/gh-2347-max-int-literals.result
index c289a80fe..f12fab708 100644
--- a/test/sql/gh-2347-max-int-literals.result
+++ b/test/sql/gh-2347-max-int-literals.result
@@ -20,9 +20,18 @@ box.sql.execute("select (-9223372036854775808)")
...
box.sql.execute("select (9223372036854775808)")
---
-- error: 'oversized integer: 9223372036854775808'
+- - [9223372036854775808]
...
box.sql.execute("select (-9223372036854775809)")
---
- error: 'oversized integer: -9223372036854775809'
...
+-- cause an overflow
+box.sql.execute("select (92233720368547758080)")
+---
+- error: 'oversized integer: 92233720368547758080'
+...
+box.sql.execute("select (-92233720368547758090)")
+---
+- error: 'oversized integer: -92233720368547758090'
+...
diff --git a/test/sql/gh-2347-max-int-literals.test.lua b/test/sql/gh-2347-max-int-literals.test.lua
index 4b1ef0d4f..7106158a9 100644
--- a/test/sql/gh-2347-max-int-literals.test.lua
+++ b/test/sql/gh-2347-max-int-literals.test.lua
@@ -9,3 +9,7 @@ box.sql.execute("select (-9223372036854775808)")
box.sql.execute("select (9223372036854775808)")
box.sql.execute("select (-9223372036854775809)")
+
+-- cause an overflow
+box.sql.execute("select (92233720368547758080)")
+box.sql.execute("select (-92233720368547758090)")
diff --git a/test/sql/integer-overflow.result b/test/sql/integer-overflow.result
index 4754c046c..8dd3a05dd 100644
--- a/test/sql/integer-overflow.result
+++ b/test/sql/integer-overflow.result
@@ -12,25 +12,25 @@ box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
--
box.sql.execute('SELECT (2147483647 * 2147483647 * 2147483647);')
---
-- error: 'Failed to execute SQL statement: integer is overflowed'
+- error: 'Failed to execute SQL statement: integer overflow'
...
box.sql.execute('SELECT (-9223372036854775808 / -1);')
---
-- error: 'Failed to execute SQL statement: integer is overflowed'
+- - [9223372036854775808]
...
box.sql.execute('SELECT (-9223372036854775808 - 1);')
---
-- error: 'Failed to execute SQL statement: integer is overflowed'
+- error: 'Failed to execute SQL statement: integer overflow'
...
box.sql.execute('SELECT (9223372036854775807 + 1);')
---
-- error: 'Failed to execute SQL statement: integer is overflowed'
+- - [9223372036854775808]
...
-- Literals are checked right after parsing.
--
box.sql.execute('SELECT 9223372036854775808;')
---
-- error: 'oversized integer: 9223372036854775808'
+- - [9223372036854775808]
...
box.sql.execute('SELECT -9223372036854775809;')
---
@@ -38,13 +38,13 @@ box.sql.execute('SELECT -9223372036854775809;')
...
box.sql.execute('SELECT 9223372036854775808 - 1;')
---
-- error: 'oversized integer: 9223372036854775808'
+- - [9223372036854775807]
...
-- Test that CAST may also leads to overflow.
--
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
@@ -69,7 +69,7 @@ box.space.T:insert({9223372036854775809})
...
box.sql.execute('SELECT * FROM t;')
---
-- error: 'Failed to execute SQL statement: integer is overflowed'
+- - [9223372036854775808]
...
box.space.T:drop()
---
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index da7b40f22..1b31e9db3 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -382,11 +382,14 @@ cn:execute(sql, parameters)
--
-- Errors during parameters binding.
--
--- Try value > INT64_MAX. sql can't bind it, since it has no
--- suitable method in its bind API.
-cn:execute('select ? as big_uint', {0xefffffffffffffff})
+-- Try value > INT64_MAX.
+cn:execute('select ? as big_uint', {0xefffffffffffffffULL})
---
-- error: Bind value for parameter 1 is out of range for type INTEGER
+- metadata:
+ - name: BIG_UINT
+ type: UNSIGNED
+ rows:
+ - [17293822569102704639]
...
-- Bind incorrect parameters.
cn:execute('select ?', { {1, 2, 3} })
diff --git a/test/sql/iproto.test.lua b/test/sql/iproto.test.lua
index fbdc5a2ae..c2c3b12e5 100644
--- a/test/sql/iproto.test.lua
+++ b/test/sql/iproto.test.lua
@@ -112,9 +112,8 @@ cn:execute(sql, parameters)
--
-- Errors during parameters binding.
--
--- Try value > INT64_MAX. sql can't bind it, since it has no
--- suitable method in its bind API.
-cn:execute('select ? as big_uint', {0xefffffffffffffff})
+-- Try value > INT64_MAX.
+cn:execute('select ? as big_uint', {0xefffffffffffffffULL})
-- Bind incorrect parameters.
cn:execute('select ?', { {1, 2, 3} })
parameters = {}
--
2.17.1
^ permalink raw reply [flat|nested] 4+ messages in thread