Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type
@ 2019-04-01 20:44 Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 01/15] sql: Convert big integers from string Stanislav Zudin
                   ` (15 more replies)
  0 siblings, 16 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

The patch enables support of big integers in the range [2^63, 2^64-1].
Conversion functions use return value to inform about value
they return - signed integer in the range [-2^63,2^63-1] or 
unsigned integer in the range [2^63, 2^64 -1].
The changes affect sql processing, VDBE, arithmetic functions and
aggregate functions.

The second version includes the following:
- New tests
- VDBE treats unsigned as a separate type.
- Refactored mapping vdbe data types to sql types
- Fixed bugs in the previous commits
- The type conversion supports unsigned integers

Issue: https://github.com/tarantool/tarantool/issues/3810
Branch: https://github.com/tarantool/tarantool/tree/stanztt/gh-3810-sql-uint64-support

Stanislav Zudin (15):
  sql: Convert big integers from string
  sql: make VDBE recognize big integers
  sql: removes unused function.
  sql: support big integers within sql binding
  sql: removes redundant function.
  sql: arithmetic functions support big integers
  sql: aggregate sql functions support big int
  sql: fixes errors
  sql: support -2^63 .. 2^64-1 integer type
  sql: support -2^63 .. 2^64-1 integer type
  sql: support -2^63 .. 2^64-1 integer type
  sql: support -2^63 .. 2^64-1 integer type
  sql: support -2^63 .. 2^64-1 integer type
  sql: support -2^63 .. 2^64-1 integer type
  sql: support -2^63 .. 2^64-1 integer type

 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                         |  27 --
 src/box/sql/os_unix.c                      |   5 -
 src/box/sql/sqlInt.h                       |  90 +++--
 src/box/sql/util.c                         | 430 ++++++++++++---------
 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 +-
 24 files changed, 866 insertions(+), 451 deletions(-)

-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 01/15] sql: Convert big integers from string
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 02/15] sql: make VDBE recognize big integers Stanislav Zudin
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Correctly handles the signed and unsigned integers on the way from sql
expression to VDBE.
VDBE doesn't distinguish yet signed and unsigned integers.

Part of #3810
---
 src/box/sql/expr.c   |  23 ++++---
 src/box/sql/main.c   |   2 +-
 src/box/sql/sqlInt.h |  63 +++++++++++------
 src/box/sql/util.c   | 160 +++++++++++++++++++------------------------
 src/box/sql/vdbe.c   |   2 +-
 5 files changed, 125 insertions(+), 125 deletions(-)

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 82688dff3..3fbfab7a0 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -3335,23 +3335,24 @@ 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)) {
+		enum atoi_result c = sql_dec_or_hex_to_i64(z, is_neg, &value);
+		switch(c) {
+		case ATOI_OVERFLOW:
 			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;
+			break;
+		case ATOI_UNSIGNED:
+		case ATOI_SIGNED:
 			sqlVdbeAddOp4Dup8(v, OP_Int64, 0, mem, 0,
-					      (u8 *)&value, P4_INT64);
+					  (u8 *)&value, P4_INT64);
+			break;
 		}
 	}
 }
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..0ef373c24 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4274,21 +4274,37 @@ 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
+};
+
+
 /**
- * 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.
+ * integer, then write that value into *val and return ATOI_SIGNED.
  *
- * 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
+ * ATOI_UNSIGNED and 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.
+ * then return ATOI_OVERFLOW.
  *
  * length is the number of bytes in the string (bytes, not
  * characters). The string is not necessarily zero-terminated.
@@ -4298,13 +4314,14 @@ field_type_sequence_dup(struct Parse *parse, enum field_type *types,
  * @param[out] val Output integer value.
  * @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
- */
-int
+ *     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_atoi64(const char *z, int64_t *val, int length);
 
 /**
@@ -4313,14 +4330,18 @@ 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.
  * @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
- */
-int
-sql_dec_or_hex_to_i64(const char *z, int64_t *val);
+ *     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);
 
 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 c89e2e8ab..be77f72f8 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -591,110 +591,56 @@ 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
+enum atoi_result
 sql_atoi64(const char *z, int64_t *val, int length)
 {
-	int incr = 1;
-	u64 u = 0;
 	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 ATOI_OVERFLOW; /* 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;
+	u64 u = strtoull(z, &end, 10);
+	if (end == z)
+		return ATOI_OVERFLOW;
+	if (errno == ERANGE)
+		return ATOI_OVERFLOW;
+
+	enum atoi_result rc;
+	if (neg) {
+		rc = ATOI_SIGNED;
+		if (u <= INT64_MAX)
+			*val = -u;
+		else if (u == INT64_MIN_MOD)
+			*val = (i64) u;
+		else
+			rc = ATOI_OVERFLOW;
 	} else {
 		*val = (i64) u;
+		rc = (u <= INT64_MAX) ? ATOI_SIGNED
+				      : ATOI_UNSIGNED;
 	}
-	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 rc;
 }
 
-int
-sql_dec_or_hex_to_i64(const char *z, int64_t *val)
+enum atoi_result
+sql_dec_or_hex_to_i64(const char *z, bool is_neg, int64_t *val)
 {
+	enum atoi_result rc;
 	if (z[0] == '0' && (z[1] == 'x' || z[1] == 'X')) {
 		uint64_t u = 0;
 		int i, k;
@@ -702,9 +648,41 @@ 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)
+			rc = ATOI_OVERFLOW;
+		else if (u > INT64_MAX)
+			rc = ATOI_UNSIGNED;
+		else
+			rc = ATOI_SIGNED;
+	}
+	else
+		rc = sql_atoi64(z, val, sqlStrlen30(z));
+
+	/* Apply sign */
+	if (is_neg) {
+		switch (rc) {
+		case ATOI_SIGNED:
+			*val = -*val;
+			break;
+		case ATOI_OVERFLOW:
+			/* n/a */
+			break;
+		case ATOI_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;
+			else
+				rc = ATOI_OVERFLOW;
+			break;
+		}
 	}
-	return sql_atoi64(z, val, sqlStrlen30(z));
+
+	return rc;
 }
 
 /*
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index c1da9a4aa..c87b10757 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -410,7 +410,7 @@ static u16 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;
-	if (sql_atoi64(pMem->z, (int64_t *)&pMem->u.i, pMem->n)==SQL_OK)
+	if (sql_atoi64(pMem->z, (int64_t *)&pMem->u.i, pMem->n)==ATOI_SIGNED)
 		return MEM_Int;
 	return MEM_Real;
 }
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 02/15] sql: make VDBE recognize big integers
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 01/15] sql: Convert big integers from string Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 03/15] sql: removes unused function Stanislav Zudin
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Adapts auxiliary functions for supporting
arithmetic operations with unsigned integers.
VDBE distinguishes signed and unsigned integers.
SELECT query correctly returns values greater
than INT64_MAX.

Part of #3810
---
 src/box/lua/sql.c     |  5 ++++-
 src/box/sql/build.c   |  4 ++--
 src/box/sql/expr.c    |  3 +++
 src/box/sql/sqlInt.h  |  3 +++
 src/box/sql/vdbe.c    | 23 ++++++++++++++++-------
 src/box/sql/vdbe.h    |  3 ++-
 src/box/sql/vdbeInt.h |  3 +++
 src/box/sql/vdbeapi.c |  8 ++++++++
 src/box/sql/vdbeaux.c | 12 +++++++-----
 src/box/sql/vdbemem.c |  5 ++++-
 10 files changed, 52 insertions(+), 17 deletions(-)

diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c
index ee20faab7..cb2927144 100644
--- a/src/box/lua/sql.c
+++ b/src/box/lua/sql.c
@@ -32,7 +32,10 @@ lua_push_row(struct lua_State *L, struct sql_stmt *stmt)
 		int type = sql_column_type(stmt, i);
 		switch (type) {
 		case SQL_INTEGER:
-			luaL_pushint64(L, sql_column_int64(stmt, i));
+			if (sql_column_is_unsigned(stmt, i))
+				luaL_pushuint64(L, sql_column_int64(stmt, i));
+			else
+				luaL_pushint64(L, sql_column_int64(stmt, i));
 			break;
 		case SQL_FLOAT:
 			lua_pushnumber(L, sql_column_double(stmt, i));
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 3fbfab7a0..fcc673436 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -3349,6 +3349,9 @@ expr_code_int(struct Parse *parse, struct Expr *expr, bool is_neg,
 			}
 			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);
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 0ef373c24..92e2f282f 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -586,6 +586,9 @@ sql_column_text(sql_stmt *,
 int
 sql_column_type(sql_stmt *, int iCol);
 
+bool
+sql_column_is_unsigned(sql_stmt *, int iCol);
+
 sql_value *
 sql_column_value(sql_stmt *,
 		     int iCol);
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index c87b10757..772e76c43 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -404,15 +404,20 @@ 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_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)==ATOI_SIGNED)
-		return MEM_Int;
-	return MEM_Real;
+	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;
+	}
 }
 
 /*
@@ -422,8 +427,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_Unsigned)) == (MEM_Int|MEM_Unsigned))
+		return (MEM_Int|MEM_Unsigned);
 	if (pMem->flags & (MEM_Int|MEM_Real)) {
 		return pMem->flags & (MEM_Int|MEM_Real);
 	}
@@ -1141,6 +1148,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_Int | MEM_Unsigned;
 	break;
 }
 
@@ -1646,8 +1655,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 */
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..66b21299a 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -248,6 +248,9 @@ 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_Unsigned  0x20000	/* Value is unsigned integer.
+				 * Combine this flag with MEM_Int
+				 * if necessary */
 #ifdef SQL_OMIT_INCRBLOB
 #undef MEM_Zero
 #define MEM_Zero 0x0000
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index d7e89073e..02a1b2e0f 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -1044,6 +1044,14 @@ sql_column_type(sql_stmt * pStmt, int i)
 	return iType;
 }
 
+bool sql_column_is_unsigned(sql_stmt * pStmt, int i)
+{
+	const struct Mem* pMem = columnMem(pStmt, i);
+	if (!pMem)
+		return false;
+	return (pMem->flags & MEM_Unsigned);
+}
+
 enum sql_subtype
 sql_column_subtype(struct sql_stmt *stmt, int i)
 {
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 0cc3c1487..37cfe0431 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;
@@ -3724,13 +3729,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_Unsigned;
 		break;
 	}
 	case MP_INT: {
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 074ff8c96..1b3dbea4a 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -489,6 +489,9 @@ sqlVdbeRealValue(Mem * pMem, double *v)
 	if (pMem->flags & MEM_Real) {
 		*v = pMem->u.r;
 		return 0;
+	} else if (pMem->flags & (MEM_Int | MEM_Unsigned)) {
+		*v = (double)(u64)pMem->u.i;
+		return 0;
 	} else if (pMem->flags & MEM_Int) {
 		*v = (double)pMem->u.i;
 		return 0;
@@ -1723,7 +1726,7 @@ mpstream_encode_vdbe_mem(struct mpstream *stream, struct Mem *var)
 	} else if (var->flags & MEM_Int) {
 		i = var->u.i;
 encode_int:
-		if (var->u.i >= 0)
+		if (var->u.i >= 0 || var->flags & MEM_Unsigned)
 			mpstream_encode_uint(stream, i);
 		else
 			mpstream_encode_int(stream, i);
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 03/15] sql: removes unused function.
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 01/15] sql: Convert big integers from string Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 02/15] sql: make VDBE recognize big integers Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 04/15] sql: support big integers within sql binding Stanislav Zudin
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Part of #3810
---
 src/box/sql/main.c | 16 ----------------
 1 file changed, 16 deletions(-)

diff --git a/src/box/sql/main.c b/src/box/sql/main.c
index 9fe2e2c9d..a3c59b126 100644
--- a/src/box/sql/main.c
+++ b/src/box/sql/main.c
@@ -1910,22 +1910,6 @@ sql_uri_boolean(const char *zFilename, const char *zParam, int bDflt)
 	return z ? sqlGetBoolean(z, bDflt) : bDflt;
 }
 
-/*
- * Return a 64-bit integer value for a query parameter.
- */
-sql_int64
-sql_uri_int64(const char *zFilename,	/* Filename as passed to xOpen */
-		  const char *zParam,	/* URI parameter sought */
-		  sql_int64 bDflt)	/* return if parameter is missing */
-{
-	const char *z = sql_uri_parameter(zFilename, zParam);
-	int64_t v;
-	if (z != NULL && sql_dec_or_hex_to_i64(z, false, &v) == 0)
-		bDflt = v;
-	return bDflt;
-}
-
-
 #ifdef SQL_ENABLE_SNAPSHOT
 /*
  * Obtain a snapshot handle for the snapshot of database zDb currently
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 04/15] sql: support big integers within sql binding
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (2 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 03/15] sql: removes unused function Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 05/15] sql: removes redundant function Stanislav Zudin
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Enables sql binding for unsigned int64.

Part of #3810
---
 src/box/execute.c     | 21 ++++++++++----
 src/box/lua/lua_sql.c |  3 ++
 src/box/lua/sql.c     | 10 ++++---
 src/box/sql/func.c    |  7 +++++
 src/box/sql/sqlInt.h  |  4 +++
 src/box/sql/vdbe.c    |  6 ++--
 src/box/sql/vdbeInt.h |  2 +-
 src/box/sql/vdbeapi.c | 66 ++++++++++++++++++++++++++++++++++++++++---
 src/box/sql/vdbemem.c | 12 +++++---
 9 files changed, 109 insertions(+), 22 deletions(-)

diff --git a/src/box/execute.c b/src/box/execute.c
index 7c77df2e5..31b89a75e 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -130,13 +130,8 @@ 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->type = (n > INT64_MAX) ? SQL_UNSIGNED : SQL_INTEGER;
 		bind->bytes = sizeof(bind->i64);
 		break;
 	}
@@ -246,6 +241,7 @@ sql_column_to_messagepack(struct sql_stmt *stmt, int i,
 	int type = sql_column_type(stmt, i);
 	switch (type) {
 	case SQL_INTEGER: {
+		assert(!sql_column_is_unsigned(stmt, i));
 		int64_t n = sql_column_int64(stmt, i);
 		if (n >= 0)
 			size = mp_sizeof_uint(n);
@@ -260,6 +256,16 @@ sql_column_to_messagepack(struct sql_stmt *stmt, int i,
 			mp_encode_int(pos, n);
 		break;
 	}
+	case SQL_UNSIGNED: {
+		assert(sql_column_is_unsigned(stmt, i));
+		int64_t n = sql_column_int64(stmt, i);
+		size = mp_sizeof_uint(n);
+		char *pos = (char *) region_alloc(region, size);
+		if (pos == NULL)
+			goto oom;
+		mp_encode_uint(pos, n);
+		break;
+	}
 	case SQL_FLOAT: {
 		double d = sql_column_double(stmt, i);
 		size = mp_sizeof_double(d);
@@ -389,6 +395,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->i64);
+		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..57a3161c7 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_int64(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 cb2927144..9a35c03aa 100644
--- a/src/box/lua/sql.c
+++ b/src/box/lua/sql.c
@@ -32,10 +32,12 @@ lua_push_row(struct lua_State *L, struct sql_stmt *stmt)
 		int type = sql_column_type(stmt, i);
 		switch (type) {
 		case SQL_INTEGER:
-			if (sql_column_is_unsigned(stmt, i))
-				luaL_pushuint64(L, sql_column_int64(stmt, i));
-			else
-				luaL_pushint64(L, sql_column_int64(stmt, i));
+			assert(!sql_column_is_unsigned(stmt, i));
+			luaL_pushint64(L, sql_column_int64(stmt, i));
+			break;
+		case SQL_UNSIGNED:
+			assert(sql_column_is_unsigned(stmt, i));
+			luaL_pushuint64(L, sql_column_int64(stmt, i));
 			break;
 		case SQL_FLOAT:
 			lua_pushnumber(L, sql_column_double(stmt, i));
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 21a69aa51..cf65bf2a2 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -141,6 +141,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 +192,11 @@ absFunc(sql_context * context, int argc, sql_value ** argv)
 			sql_result_int64(context, iVal);
 			break;
 		}
+	case SQL_UNSIGNED: {
+			i64 iVal = sql_value_int64(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 +963,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;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 92e2f282f..56aa7c681 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -644,6 +644,7 @@ enum sql_type {
 	SQL_TEXT = 3,
 	SQL_BLOB = 4,
 	SQL_NULL = 5,
+	SQL_UNSIGNED = 6
 };
 
 /**
@@ -906,6 +907,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);
 
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 772e76c43..ea9d9d98f 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -1446,7 +1446,7 @@ case OP_IntCopy: {            /* out2 */
 	pIn1 = &aMem[pOp->p1];
 	assert((pIn1->flags & MEM_Int)!=0);
 	pOut = &aMem[pOp->p2];
-	sqlVdbeMemSetInt64(pOut, pIn1->u.i);
+	sqlVdbeMemSetInt64(pOut, pIn1->u.i, (pIn1->flags & MEM_Unsigned)!=0);
 	break;
 }
 
@@ -1777,7 +1777,7 @@ integer_overflow:
 case OP_CollSeq: {
 	assert(pOp->p4type==P4_COLLSEQ || pOp->p4.pColl == NULL);
 	if (pOp->p1) {
-		sqlVdbeMemSetInt64(&aMem[pOp->p1], 0);
+		sqlVdbeMemSetInt64(&aMem[pOp->p1], 0, false);
 	}
 	break;
 }
@@ -5324,7 +5324,7 @@ case OP_AggStep: {
 	if (pCtx->skipFlag) {
 		assert(pOp[-1].opcode==OP_CollSeq);
 		i = pOp[-1].p1;
-		if (i) sqlVdbeMemSetInt64(&aMem[i], 1);
+		if (i) sqlVdbeMemSetInt64(&aMem[i], 1, false);
 	}
 	break;
 }
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index 66b21299a..0375845d9 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -477,7 +477,7 @@ void sqlVdbeMemShallowCopy(Mem *, const Mem *, int);
 void sqlVdbeMemMove(Mem *, Mem *);
 int sqlVdbeMemNulTerminate(Mem *);
 int sqlVdbeMemSetStr(Mem *, const char *, int, u8, void (*)(void *));
-void sqlVdbeMemSetInt64(Mem *, i64);
+void sqlVdbeMemSetInt64(Mem *, i64, bool);
 #ifdef SQL_OMIT_FLOATING_POINT
 #define sqlVdbeMemSetDouble sqlVdbeMemSetInt64
 #else
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index 02a1b2e0f..2c486552e 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -279,8 +279,49 @@ sql_value_type(sql_value * pVal)
 		SQL_NULL,	/* 0x1d */
 		SQL_INTEGER,	/* 0x1e */
 		SQL_NULL,	/* 0x1f */
+
+		SQL_BLOB,	/* 0x20 */
+		SQL_NULL,	/* 0x21 */
+		SQL_TEXT,	/* 0x22 */
+		SQL_NULL,	/* 0x23 */
+		SQL_UNSIGNED,	/* 0x24 */
+		SQL_NULL,	/* 0x25 */
+		SQL_UNSIGNED,	/* 0x26 */
+		SQL_NULL,	/* 0x27 */
+		SQL_FLOAT,	/* 0x28 */
+		SQL_NULL,	/* 0x29 */
+		SQL_FLOAT,	/* 0x2a */
+		SQL_NULL,	/* 0x2b */
+		SQL_INTEGER,	/* 0x2c */
+		SQL_NULL,	/* 0x2d */
+		SQL_INTEGER,	/* 0x2e */
+		SQL_NULL,	/* 0x2f */
+		SQL_BLOB,	/* 0x30 */
+		SQL_NULL,	/* 0x31 */
+		SQL_TEXT,	/* 0x32 */
+		SQL_NULL,	/* 0x33 */
+		SQL_INTEGER,	/* 0x34 */
+		SQL_NULL,	/* 0x35 */
+		SQL_INTEGER,	/* 0x36 */
+		SQL_NULL,	/* 0x37 */
+		SQL_FLOAT,	/* 0x38 */
+		SQL_NULL,	/* 0x39 */
+		SQL_FLOAT,	/* 0x3a */
+		SQL_NULL,	/* 0x3b */
+		SQL_INTEGER,	/* 0x3c */
+		SQL_NULL,	/* 0x3d */
+		SQL_INTEGER,	/* 0x3e */
+		SQL_NULL,	/* 0x3f */
 	};
-	return aType[pVal->flags & MEM_PURE_TYPE_MASK];
+
+	assert(MEM_Unsigned == 0x20000);
+	/* compress the unsigned bit with the pure
+	 * type bits, to make them applicable for
+	 * array indexing.
+	 */
+	u32 offset = (pVal->flags >> 12) | (pVal->flags & MEM_PURE_TYPE_MASK);
+	assert(offset < 0x40);
+	return aType[offset];
 }
 
 /* Make a copy of an sql_value object
@@ -401,13 +442,13 @@ 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);
+	sqlVdbeMemSetInt64(pCtx->pOut, (i64) iVal, false);
 }
 
 void
 sql_result_int64(sql_context * pCtx, i64 iVal)
 {
-	sqlVdbeMemSetInt64(pCtx->pOut, iVal);
+	sqlVdbeMemSetInt64(pCtx->pOut, iVal, false);
 }
 
 void
@@ -1370,7 +1411,20 @@ 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);
+		sqlVdbeMemSetInt64(&p->aVar[i - 1], iValue, false);
+	}
+	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, "INTEGER");
+		sqlVdbeMemSetInt64(&p->aVar[i - 1], (u64)iValue, true);
 	}
 	return rc;
 }
@@ -1418,6 +1472,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/vdbemem.c b/src/box/sql/vdbemem.c
index 1b3dbea4a..2805d7a01 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -707,11 +707,13 @@ sqlVdbeMemSetZeroBlob(Mem * pMem, int n)
  * a 64-bit integer.
  */
 static SQL_NOINLINE void
-vdbeReleaseAndSetInt64(Mem * pMem, i64 val)
+vdbeReleaseAndSetInt64(Mem * pMem, i64 val, bool is_unsigned)
 {
 	sqlVdbeMemSetNull(pMem);
 	pMem->u.i = val;
 	pMem->flags = MEM_Int;
+	if (is_unsigned)
+		pMem->flags |= MEM_Unsigned;
 }
 
 /*
@@ -719,13 +721,15 @@ vdbeReleaseAndSetInt64(Mem * pMem, i64 val)
  * manifest type INTEGER.
  */
 void
-sqlVdbeMemSetInt64(Mem * pMem, i64 val)
+sqlVdbeMemSetInt64(Mem * pMem, i64 val, bool is_unsigned)
 {
 	if (VdbeMemDynamic(pMem)) {
-		vdbeReleaseAndSetInt64(pMem, val);
+		vdbeReleaseAndSetInt64(pMem, val, is_unsigned);
 	} else {
 		pMem->u.i = val;
 		pMem->flags = MEM_Int;
+		if (is_unsigned)
+			pMem->flags |= MEM_Unsigned;
 	}
 }
 
@@ -1301,7 +1305,7 @@ valueFromExpr(sql * db,	/* The database connection */
 			goto no_mem;
 		if (ExprHasProperty(pExpr, EP_IntValue)) {
 			sqlVdbeMemSetInt64(pVal,
-					       (i64) pExpr->u.iValue * negInt);
+					       (i64) pExpr->u.iValue * negInt, false);
 		} else {
 			zVal =
 			    sqlMPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 05/15] sql: removes redundant function.
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (3 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 04/15] sql: support big integers within sql binding Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 06/15] sql: arithmetic functions support big integers Stanislav Zudin
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Part of #3810
---
 src/box/execute.c     | 2 --
 src/box/lua/sql.c     | 2 --
 src/box/sql/sqlInt.h  | 3 ---
 src/box/sql/vdbeapi.c | 8 --------
 4 files changed, 15 deletions(-)

diff --git a/src/box/execute.c b/src/box/execute.c
index 31b89a75e..210b9a228 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -241,7 +241,6 @@ sql_column_to_messagepack(struct sql_stmt *stmt, int i,
 	int type = sql_column_type(stmt, i);
 	switch (type) {
 	case SQL_INTEGER: {
-		assert(!sql_column_is_unsigned(stmt, i));
 		int64_t n = sql_column_int64(stmt, i);
 		if (n >= 0)
 			size = mp_sizeof_uint(n);
@@ -257,7 +256,6 @@ sql_column_to_messagepack(struct sql_stmt *stmt, int i,
 		break;
 	}
 	case SQL_UNSIGNED: {
-		assert(sql_column_is_unsigned(stmt, i));
 		int64_t n = sql_column_int64(stmt, i);
 		size = mp_sizeof_uint(n);
 		char *pos = (char *) region_alloc(region, size);
diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c
index 9a35c03aa..76bb44fa1 100644
--- a/src/box/lua/sql.c
+++ b/src/box/lua/sql.c
@@ -32,11 +32,9 @@ lua_push_row(struct lua_State *L, struct sql_stmt *stmt)
 		int type = sql_column_type(stmt, i);
 		switch (type) {
 		case SQL_INTEGER:
-			assert(!sql_column_is_unsigned(stmt, i));
 			luaL_pushint64(L, sql_column_int64(stmt, i));
 			break;
 		case SQL_UNSIGNED:
-			assert(sql_column_is_unsigned(stmt, i));
 			luaL_pushuint64(L, sql_column_int64(stmt, i));
 			break;
 		case SQL_FLOAT:
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 56aa7c681..9b1d7df9a 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -586,9 +586,6 @@ sql_column_text(sql_stmt *,
 int
 sql_column_type(sql_stmt *, int iCol);
 
-bool
-sql_column_is_unsigned(sql_stmt *, int iCol);
-
 sql_value *
 sql_column_value(sql_stmt *,
 		     int iCol);
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index 2c486552e..c3bdb6f86 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -1085,14 +1085,6 @@ sql_column_type(sql_stmt * pStmt, int i)
 	return iType;
 }
 
-bool sql_column_is_unsigned(sql_stmt * pStmt, int i)
-{
-	const struct Mem* pMem = columnMem(pStmt, i);
-	if (!pMem)
-		return false;
-	return (pMem->flags & MEM_Unsigned);
-}
-
 enum sql_subtype
 sql_column_subtype(struct sql_stmt *stmt, int i)
 {
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 06/15] sql: arithmetic functions support big integers
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (4 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 05/15] sql: removes redundant function Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 07/15] sql: aggregate sql functions support big int Stanislav Zudin
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Makes arithmetic functions accept arguments with
values in the range [2^63, 2^64).

Part of #3810
---
 src/box/sql/func.c    |   2 +-
 src/box/sql/sqlInt.h  |  23 +++-
 src/box/sql/util.c    | 236 ++++++++++++++++++++++++++++++++----------
 src/box/sql/vdbe.c    |  36 ++++---
 src/box/sql/vdbeInt.h |   2 +-
 5 files changed, 223 insertions(+), 76 deletions(-)

diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index cf65bf2a2..8a8acc216 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -1437,7 +1437,7 @@ sumStep(sql_context * context, int argc, sql_value ** argv)
 			i64 v = sql_value_int64(argv[0]);
 			p->rSum += v;
 			if ((p->approx | p->overflow) == 0
-			    && sqlAddInt64(&p->iSum, v)) {
+			    && sqlAddInt64(&p->iSum, true, v, true) != ATHR_SIGNED) {
 				p->overflow = 1;
 			}
 		} else {
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 9b1d7df9a..7f8e3f04e 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4383,9 +4383,26 @@ 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 be77f72f8..3786c5083 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -1249,74 +1249,202 @@ 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)
+{
+	bool is_neg = v < 0 && is_signed;
+	if (is_neg)
+		return (v == INT64_MIN) ? (u64)v : (u64)(-v);
+	else
+		return (u64)v;
+}
+
+/*
+ * Attempt to add, substract, or multiply the 64-bit value iB against
+ * the other 64-bit integer at *pA and store the result in *pA.
+ * 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 * pA, bool is_signedA, i64 iB, bool is_signedB)
 {
 	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;
+
+	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) {
+
+		assert(iA >=0 && iB < 0);
+		u64 uB = mod64(iB, true);
+
+		if ((u64)iA >= uB) {
+			u64 sum = (u64)iA - uB;
+			*pA = (i64)sum;
+			return (sum <= INT64_MAX) ? ATHR_SIGNED
+						  : ATHR_UNSIGNED;
+		} else {
+			u64 sum = uB - (u64)iA;
+			if (sum == INT64_MIN_MOD) {
+				*pA = INT64_MIN;
+			} else {
+				assert(sum < INT64_MAX);
+				*pA = -(i64)sum;
+			}
+			return ATHR_SIGNED;
+		}
+	}
+
+	if (is_negA) {
+		assert(is_signedA && is_signedB);
+		if (-(iA + LARGEST_INT64) > iB + 1)
+			return ATHR_OVERFLOW;
+		*pA = iA + iB;
+		return ATHR_SIGNED;
 	} 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;
+		if (UINT64_MAX - (u64)iA < (u64)iB)
+			return ATHR_OVERFLOW;
+
+		u64 sum = (u64)iA + (u64)iB;
+		*pA = (i64)sum;
+		return (sum <= INT64_MAX) ? ATHR_SIGNED
+					  : ATHR_UNSIGNED;
 	}
-	*pA += iB;
-	return 0;
 }
 
-int
-sqlSubInt64(i64 * pA, i64 iB)
+enum arithmetic_result
+sqlSubInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB)
 {
-	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 iA = *pA;
+
+	bool is_negA = iA < 0 && is_signedA;
+	bool is_negB = iB < 0 && is_signedB;
+
+	if (is_negA) {
+		if (!is_signedB){
+			assert((u64)iB > INT64_MAX);
+			return ATHR_OVERFLOW;
+		}
+
+		if (iB == INT64_MIN)
+			return ATHR_OVERFLOW;
+		else
+			return sqlAddInt64(pA, true, -iB, true);
+	}
+
+	if (is_negB) {
+		/* iA - (-iB) => iA + iB */
+		u64 uB = mod64(iB, true);
+		if (iB == INT64_MIN)
+			is_signedB = false;
+
+		return sqlAddInt64(pA, is_signedA, uB, is_signedB);
 	} else {
-		return sqlAddInt64(pA, -iB);
+		/* Both iA & iB are positive */
+		if ((u64)iA < (u64)iB)
+			return  ATHR_OVERFLOW;
+		u64 val = (u64)iA - (u64)iB;
+		*pA = (i64)val;
+		return (val > INT64_MAX) ? ATHR_UNSIGNED
+					 : ATHR_SIGNED;
 	}
 }
 
-int
-sqlMulInt64(i64 * pA, i64 iB)
+static enum arithmetic_result
+apply_sign(i64* pOut, u64 value, bool is_neg)
 {
-	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;
-		}
+	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 = iA * iB;
-	return 0;
+
+	*pOut = (i64) value;
+	return (value > INT64_MAX) ? ATHR_UNSIGNED
+			  	   : ATHR_SIGNED;
+}
+
+enum arithmetic_result
+sqlMulInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB)
+{
+	if (*pA == 0 || iB == 0) {
+		*pA = 0;
+		return ATHR_SIGNED;
+	}
+
+	bool is_negA = *pA < 0 && is_signedA;
+	bool is_negB = iB < 0 && is_signedB;
+
+	bool is_neg = is_negA != is_negB;
+
+	u64 uA = mod64(*pA, is_signedA);
+	u64 uB = mod64(iB, is_signedB);
+
+	if (is_neg) {
+		if (INT64_MIN_MOD / uA < uB)
+			return ATHR_OVERFLOW;
+	} else {
+		if (INT64_MAX / uA < uB)
+			return ATHR_OVERFLOW;
+	}
+
+	u64 mul = uA * uB;
+	return apply_sign(pA, mul, is_neg);
+}
+
+enum arithmetic_result
+sqlDivInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB) {
+	if (*pA == 0)
+		return ATHR_SIGNED;
+	if (iB == 0)
+		return ATHR_DIVBYZERO;
+
+	bool is_negA = *pA < 0 && is_signedA;
+	bool is_negB = iB < 0 && is_signedB;
+
+	bool is_neg = is_negA != is_negB;
+
+	u64 uA = mod64(*pA, is_signedA);
+	u64 uB = mod64(iB, is_signedB);
+
+	u64 div = uA / uB;
+	return apply_sign(pA, div, is_neg);
+}
+
+enum arithmetic_result
+sqlRemInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB) {
+
+	if (iB == 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 = *pA < 0 && is_signedA;
+	u64 uA = mod64(*pA, is_signedA);
+	u64 uB = mod64(iB, is_signedB);
+
+	u64 rem = uA % uB;
+	return apply_sign(pA, rem, is_neg);
 }
 
 /*
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index ea9d9d98f..d4bd845fb 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -1672,28 +1672,29 @@ case OP_Remainder: {           /* same as TK_REM, in1, in2, out3 */
 	if ((type1 & type2 & MEM_Int)!=0) {
 		iA = pIn1->u.i;
 		iB = pIn2->u.i;
+		bool is_signedA = (type1 & MEM_Unsigned) == 0;
+		bool is_signedB = (type2 & MEM_Unsigned) == 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_signedA, iA, is_signedB); break;
+		case OP_Subtract:  arr = sqlSubInt64(&iB, is_signedA, iA, is_signedB); break;
+		case OP_Multiply:  arr = sqlMulInt64(&iB, is_signedA, iA, is_signedB); break;
+		case OP_Divide:    arr = sqlDivInt64(&iB, is_signedA, iA, is_signedB); break;
+		default: 	   arr = sqlRemInt64(&iB, is_signedA, iA, is_signedB); 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_Int|MEM_Unsigned);
+			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) {
@@ -5177,7 +5178,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/vdbeInt.h b/src/box/sql/vdbeInt.h
index 0375845d9..42f22df52 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -276,7 +276,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_Unsigned))|f)
 
 /*
  * Return true if a memory cell is not marked as invalid.  This macro
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 07/15] sql: aggregate sql functions support big int
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (5 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 06/15] sql: arithmetic functions support big integers Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 08/15] sql: fixes errors Stanislav Zudin
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 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).

Part of #3810
---
 src/box/sql/func.c    | 35 ++++++++++++++++++++++++++++++-----
 src/box/sql/sqlInt.h  |  3 +++
 src/box/sql/vdbeapi.c |  6 ++++++
 3 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 8a8acc216..194dec252 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -1410,6 +1410,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 */
+	u8 is_unsigned;		/* True if value exceeded 2^63 */
 };
 
 /*
@@ -1433,16 +1434,38 @@ 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;
+
 		if (type == SQL_INTEGER) {
-			i64 v = sql_value_int64(argv[0]);
+			v = sql_value_int64(argv[0]);
 			p->rSum += v;
-			if ((p->approx | p->overflow) == 0
-			    && sqlAddInt64(&p->iSum, true, v, true) != ATHR_SIGNED) {
-				p->overflow = 1;
-			}
+			is_signed = true;
+		} else if (type == SQL_UNSIGNED) {
+			v = sql_value_int64(argv[0]);
+			p->rSum += (u64)v;
+			is_signed = false;
 		} else {
 			p->rSum += sql_value_double(argv[0]);
 			p->approx = 1;
+			return;
+		}
+
+		/* proceed with the integer value */
+		if ((p->approx | p->overflow) == 0) {
+			enum arithmetic_result r = sqlAddInt64(&p->iSum,
+							       p->is_unsigned == 0,
+							       v, is_signed);
+			switch (r) {
+			case ATHR_SIGNED:
+				break;
+			case ATHR_UNSIGNED:
+				p->is_unsigned = 1;
+				break;
+			default:
+				p->overflow = 1;
+				break;
+			}
 		}
 	}
 }
@@ -1455,6 +1478,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 {
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 7f8e3f04e..c43d8c193 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -495,6 +495,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 *);
 
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index c3bdb6f86..6a3413954 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -451,6 +451,12 @@ sql_result_int64(sql_context * pCtx, i64 iVal)
 	sqlVdbeMemSetInt64(pCtx->pOut, iVal, false);
 }
 
+void
+sql_result_uint64(sql_context * pCtx, u64 iVal)
+{
+	sqlVdbeMemSetInt64(pCtx->pOut, iVal, iVal > INT64_MAX);
+}
+
 void
 sql_result_null(sql_context * pCtx)
 {
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 08/15] sql: fixes errors
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (6 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 07/15] sql: aggregate sql functions support big int Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 09/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

uint64 to double casting
string to int64 conversion
sqlSubInt64
sql_value_type()
wrong arguments order in arithmetic operations.

Part of #3810
---
 src/box/sql/util.c    | 59 +++++++++++++++++++++++--------------------
 src/box/sql/vdbe.c    | 10 ++++----
 src/box/sql/vdbeInt.h |  2 ++
 src/box/sql/vdbeapi.c |  3 ++-
 src/box/sql/vdbemem.c |  2 +-
 5 files changed, 42 insertions(+), 34 deletions(-)

diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index 3786c5083..d585dc0d5 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -599,6 +599,7 @@ sqlAtoF(const char *z, double *pResult, int length)
 enum atoi_result
 sql_atoi64(const char *z, int64_t *val, int length)
 {
+	const char* expected_end = z + length;
 	int neg = 0;		/* assume positive */
 	const char *zEnd = z + length;
 	int incr = 1;
@@ -614,7 +615,7 @@ sql_atoi64(const char *z, int64_t *val, int length)
 
 	char* end = NULL;
 	u64 u = strtoull(z, &end, 10);
-	if (end == z)
+	if (end < expected_end)
 		return ATOI_OVERFLOW;
 	if (errno == ERANGE)
 		return ATOI_OVERFLOW;
@@ -1260,6 +1261,25 @@ static u64 mod64(i64 v, bool is_signed)
 		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;
+	}
+
+	*pOut = (i64) value;
+	return (value > INT64_MAX) ? ATHR_UNSIGNED
+				   : ATHR_SIGNED;
+}
+
 /*
  * Attempt to add, substract, or multiply the 64-bit value iB against
  * the other 64-bit integer at *pA and store the result in *pA.
@@ -1298,7 +1318,7 @@ sqlAddInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB)
 			if (sum == INT64_MIN_MOD) {
 				*pA = INT64_MIN;
 			} else {
-				assert(sum < INT64_MAX);
+				assert(sum <= INT64_MAX);
 				*pA = -(i64)sum;
 			}
 			return ATHR_SIGNED;
@@ -1351,32 +1371,17 @@ sqlSubInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB)
 		return sqlAddInt64(pA, is_signedA, uB, is_signedB);
 	} else {
 		/* Both iA & iB are positive */
-		if ((u64)iA < (u64)iB)
-			return  ATHR_OVERFLOW;
-		u64 val = (u64)iA - (u64)iB;
-		*pA = (i64)val;
-		return (val > INT64_MAX) ? ATHR_UNSIGNED
-					 : ATHR_SIGNED;
-	}
-}
-
-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;
+		if ((u64)iA < (u64)iB) {
+			/* subtract with sign changing */
+			u64 val = (u64)iB - (u64)iA;
+			return apply_sign(pA, val, true);
+		} else {
+			u64 val = (u64)iA - (u64)iB;
+			*pA = (i64)val;
+			return (val > INT64_MAX) ? ATHR_UNSIGNED
+						 : ATHR_SIGNED;
+		}
 	}
-
-	*pOut = (i64) value;
-	return (value > INT64_MAX) ? ATHR_UNSIGNED
-			  	   : ATHR_SIGNED;
 }
 
 enum arithmetic_result
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index d4bd845fb..ad2ce1787 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -1677,11 +1677,11 @@ case OP_Remainder: {           /* same as TK_REM, in1, in2, out3 */
 		bIntint = 1;
 		enum arithmetic_result arr;
 		switch( pOp->opcode) {
-		case OP_Add:       arr = sqlAddInt64(&iB, is_signedA, iA, is_signedB); break;
-		case OP_Subtract:  arr = sqlSubInt64(&iB, is_signedA, iA, is_signedB); break;
-		case OP_Multiply:  arr = sqlMulInt64(&iB, is_signedA, iA, is_signedB); break;
-		case OP_Divide:    arr = sqlDivInt64(&iB, is_signedA, iA, is_signedB); break;
-		default: 	   arr = sqlRemInt64(&iB, is_signedA, iA, is_signedB); 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;
 		}
 
 		switch(arr){
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index 42f22df52..2076a9a14 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -251,6 +251,8 @@ struct Mem {
 #define MEM_Unsigned  0x20000	/* Value is unsigned integer.
 				 * Combine this flag with MEM_Int
 				 * if necessary */
+#define MEM_UInt (MEM_Int | MEM_Unsigned)
+
 #ifdef SQL_OMIT_INCRBLOB
 #undef MEM_Zero
 #define MEM_Zero 0x0000
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index 6a3413954..06140569c 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -319,7 +319,8 @@ sql_value_type(sql_value * pVal)
 	 * type bits, to make them applicable for
 	 * array indexing.
 	 */
-	u32 offset = (pVal->flags >> 12) | (pVal->flags & MEM_PURE_TYPE_MASK);
+	u32 offset = ((pVal->flags & MEM_Unsigned) >> 12) |
+		     (pVal->flags & MEM_PURE_TYPE_MASK);
 	assert(offset < 0x40);
 	return aType[offset];
 }
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 2805d7a01..9e6d52b47 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -489,7 +489,7 @@ sqlVdbeRealValue(Mem * pMem, double *v)
 	if (pMem->flags & MEM_Real) {
 		*v = pMem->u.r;
 		return 0;
-	} else if (pMem->flags & (MEM_Int | MEM_Unsigned)) {
+	} else if ((pMem->flags & MEM_UInt) == MEM_UInt) {
 		*v = (double)(u64)pMem->u.i;
 		return 0;
 	} else if (pMem->flags & MEM_Int) {
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 09/15] sql: support -2^63 .. 2^64-1 integer type
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (7 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 08/15] sql: fixes errors Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 10/15] " Stanislav Zudin
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Fixes tests

Part of #3810
---
 test/sql-tap/func.test.lua               |  6 +++---
 test/sql-tap/hexlit.test.lua             |  6 ++++--
 test/sql/gh-2347-max-int-literals.result |  2 +-
 test/sql/integer-overflow.result         | 10 +++++-----
 test/sql/iproto.result                   | 11 +++++++----
 test/sql/iproto.test.lua                 |  5 ++---
 6 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 889fc5867..8e75f9c89 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -1591,14 +1591,14 @@ 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>
     })
 
@@ -1653,7 +1653,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..e6f78d244 100644
--- a/test/sql/gh-2347-max-int-literals.result
+++ b/test/sql/gh-2347-max-int-literals.result
@@ -20,7 +20,7 @@ box.sql.execute("select (-9223372036854775808)")
 ...
 box.sql.execute("select (9223372036854775808)")
 ---
-- error: 'oversized integer: 9223372036854775808'
+- - [9223372036854775808]
 ...
 box.sql.execute("select (-9223372036854775809)")
 ---
diff --git a/test/sql/integer-overflow.result b/test/sql/integer-overflow.result
index 4754c046c..aa9fabf1b 100644
--- a/test/sql/integer-overflow.result
+++ b/test/sql/integer-overflow.result
@@ -16,7 +16,7 @@ box.sql.execute('SELECT (2147483647 * 2147483647 * 2147483647);')
 ...
 box.sql.execute('SELECT (-9223372036854775808 / -1);')
 ---
-- error: 'Failed to execute SQL statement: integer is overflowed'
+- - [9223372036854775808]
 ...
 box.sql.execute('SELECT (-9223372036854775808 - 1);')
 ---
@@ -24,13 +24,13 @@ box.sql.execute('SELECT (-9223372036854775808 - 1);')
 ...
 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,7 +38,7 @@ box.sql.execute('SELECT -9223372036854775809;')
 ...
 box.sql.execute('SELECT 9223372036854775808 - 1;')
 ---
-- error: 'oversized integer: 9223372036854775808'
+- - [9223372036854775807]
 ...
 -- Test that CAST may also leads to overflow.
 --
@@ -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..deb0c5309 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: INTEGER
+  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] 17+ messages in thread

* [tarantool-patches] [PATCH v2 10/15] sql: support -2^63 .. 2^64-1 integer type
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (8 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 09/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 11/15] " Stanislav Zudin
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Fixes an error in the conversion functions.
The cast to integer didn't take into account
the 'unsigned' bit.
Fixes the error in overflow check inside the sqlMulInt64().
Makes the overflow check more precisely in sql_atoi64().
Fixes the error message, and affected tests.

Part of #3810
---
 src/box/sql/util.c               |  5 +-
 src/box/sql/vdbe.c               | 24 ++++++---
 src/box/sql/vdbeInt.h            |  2 +-
 src/box/sql/vdbeapi.c            |  6 ++-
 src/box/sql/vdbemem.c            | 86 ++++++++++++++++++++------------
 test/sql/integer-overflow.result |  4 +-
 6 files changed, 80 insertions(+), 47 deletions(-)

diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index d585dc0d5..1f3f92a04 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -614,10 +614,11 @@ sql_atoi64(const char *z, int64_t *val, int length)
 	}
 
 	char* end = NULL;
+	errno = 0;
 	u64 u = strtoull(z, &end, 10);
 	if (end < expected_end)
 		return ATOI_OVERFLOW;
-	if (errno == ERANGE)
+	if (errno != 0)
 		return ATOI_OVERFLOW;
 
 	enum atoi_result rc;
@@ -1404,7 +1405,7 @@ sqlMulInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB)
 		if (INT64_MIN_MOD / uA < uB)
 			return ATHR_OVERFLOW;
 	} else {
-		if (INT64_MAX / uA < uB)
+		if (UINT64_MAX / uA < uB)
 			return ATHR_OVERFLOW;
 	}
 
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index ad2ce1787..997d0a1ab 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -1755,7 +1755,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;
 }
@@ -1941,13 +1941,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;
@@ -1961,6 +1963,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);
@@ -2468,7 +2474,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;
@@ -2481,7 +2488,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;
@@ -2519,7 +2527,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;
@@ -2544,7 +2553,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;
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index 2076a9a14..46094929f 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -490,7 +490,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 06140569c..d8f9d9b87 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,8 @@ 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;
 }
 
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 9e6d52b47..e4ea987cb 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -411,39 +411,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 +455,30 @@ 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 = (flags & MEM_Unsigned) != 0;
 		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)
+		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 -1;
 }
@@ -514,9 +521,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_Int | MEM_Unsigned);
+		else
+			MemSetTypeFlag(pMem, MEM_Int);
 	}
 	return rc;
 }
@@ -530,15 +542,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_Int | MEM_Unsigned);
+		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_Int | MEM_Unsigned);
+		else
+			MemSetTypeFlag(pMem, MEM_Int);
 		return 0;
 	}
 
diff --git a/test/sql/integer-overflow.result b/test/sql/integer-overflow.result
index aa9fabf1b..b6cacbeff 100644
--- a/test/sql/integer-overflow.result
+++ b/test/sql/integer-overflow.result
@@ -12,7 +12,7 @@ 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);')
 ---
@@ -20,7 +20,7 @@ box.sql.execute('SELECT (-9223372036854775808 / -1);')
 ...
 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);')
 ---
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 11/15] sql: support -2^63 .. 2^64-1 integer type
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (9 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 10/15] " Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 12/15] " Stanislav Zudin
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Removes unused code.

Part of #3810
---
 src/box/sql/main.c    | 11 -----------
 src/box/sql/os_unix.c |  5 -----
 src/box/sql/sqlInt.h  |  9 ---------
 src/box/sql/util.c    | 35 -----------------------------------
 4 files changed, 60 deletions(-)

diff --git a/src/box/sql/main.c b/src/box/sql/main.c
index a3c59b126..03dbaf842 100644
--- a/src/box/sql/main.c
+++ b/src/box/sql/main.c
@@ -1899,17 +1899,6 @@ sql_uri_parameter(const char *zFilename, const char *zParam)
 	return 0;
 }
 
-/*
- * Return a boolean value for a query parameter.
- */
-int
-sql_uri_boolean(const char *zFilename, const char *zParam, int bDflt)
-{
-	const char *z = sql_uri_parameter(zFilename, zParam);
-	bDflt = bDflt != 0;
-	return z ? sqlGetBoolean(z, bDflt) : bDflt;
-}
-
 #ifdef SQL_ENABLE_SNAPSHOT
 /*
  * Obtain a snapshot handle for the snapshot of database zDb currently
diff --git a/src/box/sql/os_unix.c b/src/box/sql/os_unix.c
index b6599852a..615d539b5 100644
--- a/src/box/sql/os_unix.c
+++ b/src/box/sql/os_unix.c
@@ -1693,11 +1693,6 @@ getFileMode(const char *zFile,	/* File name */
  * corresponding database file and sets *pMode to this value. Whenever
  * possible, journal files are created using the same permissions
  * as the associated database file.
- *
- * If the SQL_ENABLE_8_3_NAMES option is enabled, then the
- * original filename is unavailable.  But 8_3_NAMES is only used for
- * FAT filesystems and permissions do not matter there, so just use
- * the default permissions.
  */
 static int
 findCreateFileMode(const char *zPath,	/* Path of file (possibly) being created */
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index c43d8c193..df051a32b 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -847,10 +847,6 @@ sql_limit(sql *, int id, int newVal);
 #define SQL_SYNC_FULL          0x00003
 #define SQL_SYNC_DATAONLY      0x00010
 
-int
-sql_uri_boolean(const char *zFile,
-		    const char *zParam, int bDefault);
-
 extern char *
 sql_temp_directory;
 
@@ -4407,11 +4403,6 @@ 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 *);
-#else
-#define sqlFileSuffix3(X,Y)
-#endif
 u8 sqlGetBoolean(const char *z, u8);
 
 const void *sqlValueText(sql_value *);
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index 1f3f92a04..62c43c555 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -1467,41 +1467,6 @@ sqlAbsInt32(int x)
 	return -x;
 }
 
-#ifdef SQL_ENABLE_8_3_NAMES
-/*
- * If SQL_ENABLE_8_3_NAMES is set at compile-time and if the database
- * filename in zBaseFilename is a URI with the "8_3_names=1" parameter and
- * if filename in z[] has a suffix (a.k.a. "extension") that is longer than
- * three characters, then shorten the suffix on z[] to be the last three
- * characters of the original suffix.
- *
- * If SQL_ENABLE_8_3_NAMES is set to 2 at compile-time, then always
- * do the suffix shortening regardless of URI parameter.
- *
- * Examples:
- *
- *     test.db-journal    =>   test.nal
- *     test.db-wal        =>   test.wal
- *     test.db-shm        =>   test.shm
- *     test.db-mj7f3319fa =>   test.9fa
- */
-void
-sqlFileSuffix3(const char *zBaseFilename, char *z)
-{
-#if SQL_ENABLE_8_3_NAMES<2
-	if (sql_uri_boolean(zBaseFilename, "8_3_names", 0))
-#endif
-	{
-		int i, sz;
-		sz = sqlStrlen30(z);
-		for (i = sz - 1; i > 0 && z[i] != '/' && z[i] != '.'; i--) {
-		}
-		if (z[i] == '.' && ALWAYS(sz > i + 4))
-			memmove(&z[i + 1], &z[sz - 3], 4);
-	}
-}
-#endif
-
 /*
  * Find (an approximate) sum of two LogEst values.  This computation is
  * not a simple "+" operator because LogEst is stored as a logarithmic
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 12/15] sql: support -2^63 .. 2^64-1 integer type
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (10 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 11/15] " Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 13/15] " Stanislav Zudin
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 13/15] sql: support -2^63 .. 2^64-1 integer type
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (11 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 12/15] " Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 14/15] " Stanislav Zudin
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Returns correct text name for unsigned data type.
Applies a coding standard.
Improves mapping of vdbe data types to sql data types.

Part of #3810
---
 src/box/sql/func.c     |  11 +--
 src/box/sql/sqlInt.h   |  15 ++--
 src/box/sql/util.c     | 153 +++++++++++++++++++++--------------------
 src/box/sql/vdbe.c     |   2 +
 src/box/sql/vdbeInt.h  |   4 +-
 src/box/sql/vdbeapi.c  | 109 +++++++++--------------------
 test/sql/iproto.result |   2 +-
 7 files changed, 132 insertions(+), 164 deletions(-)

diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index f1b894f16..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;
@@ -1410,7 +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 */
-	u8 is_unsigned;		/* True if value exceeded 2^63 */
+	bool is_unsigned;	/* True if value exceeded 2^63 */
 };
 
 /*
@@ -1441,14 +1444,14 @@ sumStep(sql_context * context, int argc, sql_value ** argv)
 			p->rSum += v;
 			if ((p->approx | p->overflow) == 0)
 				rc = sqlAddInt64(&p->iSum,
-						p->is_unsigned == 0,
+						!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 == 0,
+						 !p->is_unsigned,
 						 v, false);
 		} else {
 			p->rSum += sql_value_double(argv[0]);
@@ -1462,7 +1465,7 @@ sumStep(sql_context * context, int argc, sql_value ** argv)
 			case ATHR_SIGNED:
 				break;
 			case ATHR_UNSIGNED:
-				p->is_unsigned = 1;
+				p->is_unsigned = true;
 				break;
 			default:
 				p->overflow = 1;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index fd87366df..509879cf7 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4383,11 +4383,16 @@ enum arithmetic_result {
 	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);
+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);
 u8 sqlGetBoolean(const char *z, u8);
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index ce3f1b5c3..e56ae6e05 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -1275,102 +1275,103 @@ apply_sign(i64* pOut, u64 value, bool is_neg)
 }
 
 /*
- * Attempt to add, substract, or multiply the 64-bit value iB against
- * the other 64-bit integer at *pA and store the result in *pA.
+ * 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 * pA, bool is_signedA, i64 iB, bool is_signedB)
+sqlAddInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed)
 {
-	i64 iA = *pA;
+	i64 ilhs = *plhs;
 
-	bool is_negA = iA < 0 && is_signedA;
-	bool is_negB = iB < 0 && is_signedB;
+	bool is_lhs_neg = ilhs < 0 && is_lhs_signed;
+	bool is_rhs_neg = irhs < 0 && is_rhs_signed;
 
-	if (is_negA != is_negB) {
-		/* Make sure we've got only one combination of
+	if (is_lhs_neg != is_rhs_neg) {
+		/*
+		 * 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_lhs_neg > is_rhs_neg) {
+			SWAP(is_lhs_neg, is_rhs_neg);
+			SWAP(ilhs, irhs);
 		}
-		assert(is_negA == false && is_negB == true);
+		assert(is_lhs_neg == false && is_rhs_neg == true);
 
-		u64 uB = mod64(iB, true);
+		u64 uB = mod64(irhs, true);
 
-		if ((u64)iA >= uB) {
-			u64 sum = (u64)iA - uB;
-			*pA = (i64)sum;
+		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)iA;
+			u64 sum = uB - (u64)ilhs;
 			if (sum == INT64_MIN_MOD) {
-				*pA = INT64_MIN;
+				*plhs = INT64_MIN;
 			} else {
 				assert(sum <= INT64_MAX);
-				*pA = -(i64)sum;
+				*plhs = -(i64)sum;
 			}
 			return ATHR_SIGNED;
 		}
 	}
 
-	if (is_negA) {
-		assert(is_signedA && is_signedB);
-		if (-(iA + LARGEST_INT64) > iB + 1)
+	if (is_lhs_neg) {
+		assert(is_lhs_signed && is_rhs_signed);
+		if (-(ilhs + LARGEST_INT64) > irhs + 1)
 			return ATHR_OVERFLOW;
-		*pA = iA + iB;
+		*plhs = ilhs + irhs;
 		return ATHR_SIGNED;
 	} else {
-		if (UINT64_MAX - (u64)iA < (u64)iB)
+		if (UINT64_MAX - (u64)ilhs < (u64)irhs)
 			return ATHR_OVERFLOW;
 
-		u64 sum = (u64)iA + (u64)iB;
-		*pA = (i64)sum;
+		u64 sum = (u64)ilhs + (u64)irhs;
+		*plhs = (i64)sum;
 		return (sum <= INT64_MAX) ? ATHR_SIGNED
 					  : ATHR_UNSIGNED;
 	}
 }
 
 enum arithmetic_result
-sqlSubInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB)
+sqlSubInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed)
 {
-	i64 iA = *pA;
+	i64 ilhs = *plhs;
 
-	bool is_negA = iA < 0 && is_signedA;
-	bool is_negB = iB < 0 && is_signedB;
+	bool is_lhs_neg = ilhs < 0 && is_lhs_signed;
+	bool is_rhs_neg = irhs < 0 && is_rhs_signed;
 
-	if (is_negA) {
-		if (!is_signedB){
-			assert((u64)iB > INT64_MAX);
+	if (is_lhs_neg) {
+		if (!is_rhs_signed){
+			assert((u64)irhs > INT64_MAX);
 			return ATHR_OVERFLOW;
 		}
 
-		if (iB == INT64_MIN)
+		if (irhs == INT64_MIN)
 			return ATHR_OVERFLOW;
 		else
-			return sqlAddInt64(pA, true, -iB, true);
+			return sqlAddInt64(plhs, true, -irhs, true);
 	}
 
-	if (is_negB) {
-		/* iA - (-iB) => iA + iB */
-		u64 uB = mod64(iB, true);
-		if (iB == INT64_MIN)
-			is_signedB = false;
+	if (is_rhs_neg) {
+		/* ilhs - (-irhs) => ilhs + irhs */
+		u64 uB = mod64(irhs, true);
+		if (irhs == INT64_MIN)
+			is_rhs_signed = false;
 
-		return sqlAddInt64(pA, is_signedA, uB, is_signedB);
+		return sqlAddInt64(plhs, is_lhs_signed, uB, is_rhs_signed);
 	} else {
-		/* Both iA & iB are positive */
-		if ((u64)iA < (u64)iB) {
+		/* Both ilhs & irhs are positive */
+		if ((u64)ilhs < (u64)irhs) {
 			/* subtract with sign changing */
-			u64 val = (u64)iB - (u64)iA;
-			return apply_sign(pA, val, true);
+			u64 val = (u64)irhs - (u64)ilhs;
+			return apply_sign(plhs, val, true);
 		} else {
-			u64 val = (u64)iA - (u64)iB;
-			*pA = (i64)val;
+			u64 val = (u64)ilhs - (u64)irhs;
+			*plhs = (i64)val;
 			return (val > INT64_MAX) ? ATHR_UNSIGNED
 						 : ATHR_SIGNED;
 		}
@@ -1378,56 +1379,56 @@ sqlSubInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB)
 }
 
 enum arithmetic_result
-sqlMulInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB)
+sqlMulInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed)
 {
-	if (*pA == 0 || iB == 0) {
-		*pA = 0;
+	if (*plhs == 0 || irhs == 0) {
+		*plhs = 0;
 		return ATHR_SIGNED;
 	}
 
-	bool is_negA = *pA < 0 && is_signedA;
-	bool is_negB = iB < 0 && is_signedB;
+	bool is_lhs_neg = *plhs < 0 && is_lhs_signed;
+	bool is_rhs_neg = irhs < 0 && is_rhs_signed;
 
-	bool is_neg = is_negA != is_negB;
+	bool is_neg = is_lhs_neg != is_rhs_neg;
 
-	u64 uA = mod64(*pA, is_signedA);
-	u64 uB = mod64(iB, is_signedB);
+	u64 ulhs = mod64(*plhs, is_lhs_signed);
+	u64 urhs = mod64(irhs, is_rhs_signed);
 
 	if (is_neg) {
-		if (INT64_MIN_MOD / uA < uB)
+		if (INT64_MIN_MOD / ulhs < urhs)
 			return ATHR_OVERFLOW;
 	} else {
-		if (UINT64_MAX / uA < uB)
+		if (UINT64_MAX / ulhs < urhs)
 			return ATHR_OVERFLOW;
 	}
 
-	u64 mul = uA * uB;
-	return apply_sign(pA, mul, is_neg);
+	u64 mul = ulhs * urhs;
+	return apply_sign(plhs, mul, is_neg);
 }
 
 enum arithmetic_result
-sqlDivInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB) {
-	if (*pA == 0)
+sqlDivInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed) {
+	if (*plhs == 0)
 		return ATHR_SIGNED;
-	if (iB == 0)
+	if (irhs == 0)
 		return ATHR_DIVBYZERO;
 
-	bool is_negA = *pA < 0 && is_signedA;
-	bool is_negB = iB < 0 && is_signedB;
+	bool is_lhs_neg = *plhs < 0 && is_lhs_signed;
+	bool is_rhs_neg = irhs < 0 && is_rhs_signed;
 
-	bool is_neg = is_negA != is_negB;
+	bool is_neg = is_lhs_neg != is_rhs_neg;
 
-	u64 uA = mod64(*pA, is_signedA);
-	u64 uB = mod64(iB, is_signedB);
+	u64 ulhs = mod64(*plhs, is_lhs_signed);
+	u64 urhs = mod64(irhs, is_rhs_signed);
 
-	u64 div = uA / uB;
-	return apply_sign(pA, div, is_neg);
+	u64 div = ulhs / urhs;
+	return apply_sign(plhs, div, is_neg);
 }
 
 enum arithmetic_result
-sqlRemInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB) {
+sqlRemInt64(i64 * plhs, bool is_lhs_signed, i64 irhs, bool is_rhs_signed) {
 
-	if (iB == 0)
+	if (irhs == 0)
 		return ATHR_DIVBYZERO;
 	/*
 	 * The sign of the remainder is defined in such
@@ -1437,12 +1438,12 @@ sqlRemInt64(i64 * pA, bool is_signedA, i64 iB, bool is_signedB) {
 	 * The 2nd operand doesn't affect the sign of result.
 	 */
 
-	bool is_neg = *pA < 0 && is_signedA;
-	u64 uA = mod64(*pA, is_signedA);
-	u64 uB = mod64(iB, is_signedB);
+	bool is_neg = *plhs < 0 && is_lhs_signed;
+	u64 ulhs = mod64(*plhs, is_lhs_signed);
+	u64 urhs = mod64(irhs, is_rhs_signed);
 
-	u64 rem = uA % uB;
-	return apply_sign(pA, rem, is_neg);
+	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 3df5006c8..787fdadab 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -646,6 +646,8 @@ mem_type_to_str(const struct Mem *p)
 		return "BLOB";
 	case MEM_Bool:
 		return "BOOLEAN";
+	case MEM_UInt:
+		return "UNSIGNED";
 	default:
 		unreachable();
 	}
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index f986d136c..78e211c97 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -265,7 +265,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
 };
 
 
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index 66fceb444..fe948dd26 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -257,83 +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 */
-
-		SQL_BLOB,	/* 0x20 */
-		SQL_NULL,	/* 0x21 */
-		SQL_TEXT,	/* 0x22 */
-		SQL_NULL,	/* 0x23 */
-		SQL_UNSIGNED,	/* 0x24 */
-		SQL_NULL,	/* 0x25 */
-		SQL_UNSIGNED,	/* 0x26 */
-		SQL_NULL,	/* 0x27 */
-		SQL_FLOAT,	/* 0x28 */
-		SQL_NULL,	/* 0x29 */
-		SQL_FLOAT,	/* 0x2a */
-		SQL_NULL,	/* 0x2b */
-		SQL_INTEGER,	/* 0x2c */
-		SQL_NULL,	/* 0x2d */
-		SQL_INTEGER,	/* 0x2e */
-		SQL_NULL,	/* 0x2f */
-		SQL_BLOB,	/* 0x30 */
-		SQL_NULL,	/* 0x31 */
-		SQL_TEXT,	/* 0x32 */
-		SQL_NULL,	/* 0x33 */
-		SQL_INTEGER,	/* 0x34 */
-		SQL_NULL,	/* 0x35 */
-		SQL_INTEGER,	/* 0x36 */
-		SQL_NULL,	/* 0x37 */
-		SQL_FLOAT,	/* 0x38 */
-		SQL_NULL,	/* 0x39 */
-		SQL_FLOAT,	/* 0x3a */
-		SQL_NULL,	/* 0x3b */
-		SQL_INTEGER,	/* 0x3c */
-		SQL_NULL,	/* 0x3d */
-		SQL_INTEGER,	/* 0x3e */
-		SQL_NULL,	/* 0x3f */
-	};
-
-	assert(MEM_Unsigned == 0x20000);
-	/* compress the unsigned bit with the pure
-	 * type bits, to make them applicable for
-	 * array indexing.
+	/*
+	 * 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
 	 */
-	u32 offset = ((pVal->flags & MEM_Unsigned) >> 12) |
-		     (pVal->flags & MEM_PURE_TYPE_MASK);
-	assert(offset < 0x40);
-	return aType[offset];
+	if ((pVal->flags & MEM_Null) != 0)
+		return SQL_NULL;
+	if ((pVal->flags & MEM_Unsigned) != 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
@@ -1444,7 +1399,7 @@ sql_bind_uint64(sql_stmt * pStmt, int i, sql_uint64 iValue)
 	Vdbe *p = (Vdbe *) pStmt;
 	rc = vdbeUnbind(p, i);
 	if (rc == SQL_OK) {
-		rc = sql_bind_type(p, i, "INTEGER");
+		rc = sql_bind_type(p, i, "UNSIGNED");
 		sqlVdbeMemSetUInt64(&p->aVar[i - 1], iValue);
 	}
 	return rc;
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index deb0c5309..1b31e9db3 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -387,7 +387,7 @@ cn:execute('select ? as big_uint', {0xefffffffffffffffULL})
 ---
 - metadata:
   - name: BIG_UINT
-    type: INTEGER
+    type: UNSIGNED
   rows:
   - [17293822569102704639]
 ...
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 14/15] sql: support -2^63 .. 2^64-1 integer type
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (12 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 13/15] " Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 15/15] " Stanislav Zudin
  2019-04-02 16:54 ` [tarantool-patches] Re: [PATCH v2 00/15] " n.pettik
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Adds tests.

Part of #3810
---
 test/sql-tap/func.test.lua                 | 14 +++++++++++++-
 test/sql/gh-2347-max-int-literals.result   |  9 +++++++++
 test/sql/gh-2347-max-int-literals.test.lua |  4 ++++
 3 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index c77b183db..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
@@ -1602,6 +1602,7 @@ test:do_execsql_test(
         -- </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",
     [[
diff --git a/test/sql/gh-2347-max-int-literals.result b/test/sql/gh-2347-max-int-literals.result
index e6f78d244..f12fab708 100644
--- a/test/sql/gh-2347-max-int-literals.result
+++ b/test/sql/gh-2347-max-int-literals.result
@@ -26,3 +26,12 @@ 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)")
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] [PATCH v2 15/15] sql: support -2^63 .. 2^64-1 integer type
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (13 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 14/15] " Stanislav Zudin
@ 2019-04-01 20:44 ` Stanislav Zudin
  2019-04-02 16:54 ` [tarantool-patches] Re: [PATCH v2 00/15] " n.pettik
  15 siblings, 0 replies; 17+ messages in thread
From: Stanislav Zudin @ 2019-04-01 20:44 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

Makes VDBE treat unsigned as a separate type.

Closes #3810
---
 src/box/sql/vdbe.c      | 94 +++++++++++++++++++++++++++------------
 src/box/sql/vdbeInt.h   |  9 ++--
 src/box/sql/vdbeapi.c   |  2 +-
 src/box/sql/vdbeaux.c   | 98 ++++++++++++++++++++++++++++++++++++-----
 src/box/sql/vdbemem.c   | 47 ++++++++++++--------
 src/box/sql/vdbetrace.c |  2 +
 6 files changed, 187 insertions(+), 65 deletions(-)

diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 787fdadab..a148bb7d4 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -293,13 +293,13 @@ 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;
 	bool is_unsigned = false;
 	if (0 == sql_atoi64(pRec->z, (int64_t *)&iValue,
 		&is_unsigned, pRec->n)) {
 		pRec->u.i = iValue;
-		pRec->flags |= is_unsigned ? (MEM_Int | MEM_Unsigned)
+		pRec->flags |= is_unsigned ? MEM_UInt
 					   : MEM_Int;
 	} else {
 		pRec->u.r = rValue;
@@ -338,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) {
@@ -350,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:
@@ -361,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;
@@ -409,7 +429,7 @@ sql_value_apply_type(
  */
 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;
@@ -417,7 +437,7 @@ static u32 SQL_NOINLINE computeNumericType(Mem *pMem)
 	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)
+	return is_unsigned ? MEM_UInt
 			   : MEM_Int;
 }
 
@@ -430,10 +450,8 @@ static u32 SQL_NOINLINE computeNumericType(Mem *pMem)
  */
 static u32 numericType(Mem *pMem)
 {
-	if ((pMem->flags & (MEM_Int|MEM_Unsigned)) == (MEM_Int|MEM_Unsigned))
-		return (MEM_Int|MEM_Unsigned);
-	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);
@@ -538,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);
@@ -1152,7 +1174,7 @@ case OP_Int64: {           /* out2 */
 	assert(pOp->p4.pI64!=0);
 	pOut->u.i = *pOp->p4.pI64;
 	if (pOp->p4type == P4_UINT64)
-		pOut->flags = MEM_Int | MEM_Unsigned;
+		pOut->flags = MEM_UInt;
 	break;
 }
 
@@ -1447,9 +1469,9 @@ case OP_SCopy: {            /* out2 */
  */
 case OP_IntCopy: {            /* out2 */
 	pIn1 = &aMem[pOp->p1];
-	assert((pIn1->flags & MEM_Int)!=0);
+	assert((pIn1->flags & (MEM_Int|MEM_UInt))!=0);
 	pOut = &aMem[pOp->p2];
-	if (pIn1->flags & MEM_Unsigned)
+	if (pIn1->flags & MEM_UInt)
 		sqlVdbeMemSetUInt64(pOut, pIn1->u.u);
 	else
 		sqlVdbeMemSetInt64(pOut, pIn1->u.i);
@@ -1675,11 +1697,11 @@ 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_Unsigned) == 0;
-		bool is_signedB = (type2 & MEM_Unsigned) == 0;
+		bool is_signedA = (type1 & MEM_UInt) == 0;
+		bool is_signedB = (type2 & MEM_UInt) == 0;
 		bIntint = 1;
 		enum arithmetic_result arr;
 		switch( pOp->opcode) {
@@ -1695,7 +1717,7 @@ case OP_Remainder: {           /* same as TK_REM, in1, in2, out3 */
 			MemSetTypeFlag(pOut, MEM_Int);
 			break;
 		case ATHR_UNSIGNED:
-			MemSetTypeFlag(pOut, MEM_Int|MEM_Unsigned);
+			MemSetTypeFlag(pOut, MEM_UInt);
 			break;
 		case ATHR_OVERFLOW:	goto integer_overflow;
 		case ATHR_DIVBYZERO:	goto division_by_zero;
@@ -2024,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);
@@ -2052,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;
@@ -2229,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,
@@ -2255,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));
@@ -3559,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. */
 
@@ -3587,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;
@@ -3611,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
@@ -3669,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);
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index 78e211c97..5b2d129f7 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -235,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
@@ -249,10 +249,7 @@ struct Mem {
 #define MEM_Agg       0x4000	/* Mem.z points to an agg function context */
 #define MEM_Zero      0x8000	/* Mem.i contains count of 0s appended to blob */
 #define MEM_Subtype   0x10000	/* Mem.eSubtype is valid */
-#define MEM_Unsigned  0x20000	/* Value is unsigned integer.
-				 * Combine this flag with MEM_Int
-				 * if necessary */
-#define MEM_UInt (MEM_Int | MEM_Unsigned)
+#define MEM_UInt      0x20000	/* Value is unsigned integer. */
 
 #ifdef SQL_OMIT_INCRBLOB
 #undef MEM_Zero
@@ -281,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|MEM_Unsigned))|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
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index fe948dd26..e138c5abd 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -276,7 +276,7 @@ sql_value_type(sql_value * pVal)
 	 */
 	if ((pVal->flags & MEM_Null) != 0)
 		return SQL_NULL;
-	if ((pVal->flags & MEM_Unsigned) != 0)
+	if ((pVal->flags & MEM_UInt) != 0)
 		return SQL_UNSIGNED;
 	if ((pVal->flags & MEM_Int) != 0)
 		return SQL_INTEGER;
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 37cfe0431..3fbce2f01 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -1373,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) {
@@ -2856,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;
@@ -3362,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
@@ -3390,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;
@@ -3398,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;
@@ -3409,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;
 			}
@@ -3416,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;
 			}
@@ -3572,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);
@@ -3592,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;
 			}
@@ -3732,7 +3810,7 @@ vdbe_decode_msgpack_into_mem(const char *buf, struct Mem *mem, uint32_t *len)
 		mem->u.i = v;
 		mem->flags = MEM_Int;
 		if (v > INT64_MAX)
-			mem->flags |= MEM_Unsigned;
+			mem->flags = MEM_UInt;
 		break;
 	}
 	case MP_INT: {
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 2d61ca675..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;
 }
 
@@ -462,7 +465,11 @@ sqlVdbeIntValue(Mem * pMem, int64_t *i, bool *is_unsigned)
 	flags = pMem->flags;
 	if (flags & MEM_Int) {
 		*i = pMem->u.i;
-		*is_unsigned = (flags & MEM_Unsigned) != 0;
+		*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, is_unsigned);
@@ -486,8 +493,8 @@ sqlVdbeRealValue(Mem * pMem, double *v)
 	if (pMem->flags & MEM_Real) {
 		*v = pMem->u.r;
 		return 0;
-	} else if ((pMem->flags & MEM_UInt) == MEM_UInt) {
-		*v = (double)(u64)pMem->u.i;
+	} else if (pMem->flags & MEM_UInt) {
+		*v = (double)pMem->u.u;
 		return 0;
 	} else if (pMem->flags & MEM_Int) {
 		*v = (double)pMem->u.i;
@@ -516,7 +523,7 @@ mem_apply_integer_type(Mem *pMem)
 	if ((rc = doubleToInt64(pMem->u.r, (int64_t *) &ix, &is_unsigned)) == 0) {
 		pMem->u.i = ix;
 		if (is_unsigned)
-			MemSetTypeFlag(pMem, MEM_Int | MEM_Unsigned);
+			MemSetTypeFlag(pMem, MEM_UInt);
 		else
 			MemSetTypeFlag(pMem, MEM_Int);
 	}
@@ -537,7 +544,7 @@ sqlVdbeMemIntegerify(Mem * pMem, bool is_forced)
 		pMem->u.i = i;
 
 		if (is_unsigned)
-			MemSetTypeFlag(pMem, MEM_Int | MEM_Unsigned);
+			MemSetTypeFlag(pMem, MEM_UInt);
 		else
 			MemSetTypeFlag(pMem, MEM_Int);
 		return 0;
@@ -546,7 +553,7 @@ sqlVdbeMemIntegerify(Mem * pMem, bool is_forced)
 			return -1;
 
 		if (is_unsigned)
-			MemSetTypeFlag(pMem, MEM_Int | MEM_Unsigned);
+			MemSetTypeFlag(pMem, MEM_UInt);
 		else
 			MemSetTypeFlag(pMem, MEM_Int);
 		return 0;
@@ -589,7 +596,7 @@ 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);
 		bool is_unsigned = false;
 		if (0 == sql_atoi64(pMem->z,
@@ -597,7 +604,7 @@ sqlVdbeMemNumerify(Mem * pMem)
 				    &is_unsigned,
 				    pMem->n)) {
 			if (is_unsigned)
-				MemSetTypeFlag(pMem, MEM_Int | MEM_Unsigned);
+				MemSetTypeFlag(pMem, MEM_UInt);
 			else
 				MemSetTypeFlag(pMem, MEM_Int);
 		} else {
@@ -609,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;
 }
@@ -652,7 +659,7 @@ sqlVdbeMemCast(Mem * pMem, enum field_type type)
 				       pMem->n) != 0)
 				return -1;
 			if (is_unsigned)
-				MemSetTypeFlag(pMem, MEM_Int | MEM_Unsigned);
+				MemSetTypeFlag(pMem, MEM_UInt);
 			else
 				MemSetTypeFlag(pMem, MEM_Int);
 			return 0;
@@ -666,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;
 	}
 }
@@ -747,7 +754,7 @@ vdbeReleaseAndSetUInt64(Mem * pMem, u64 val)
 {
 	sqlVdbeMemSetNull(pMem);
 	pMem->u.u = val;
-	pMem->flags = MEM_Int | MEM_Unsigned;
+	pMem->flags = MEM_UInt;
 }
 
 /*
@@ -772,7 +779,7 @@ sqlVdbeMemSetUInt64(Mem * pMem, u64 val)
 		vdbeReleaseAndSetUInt64(pMem, val);
 	} else {
 		pMem->u.u = val;
-		pMem->flags = MEM_Int | MEM_Unsigned;
+		pMem->flags = MEM_UInt;
 	}
 }
 
@@ -1362,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) */
@@ -1773,10 +1780,12 @@ mpstream_encode_vdbe_mem(struct mpstream *stream, struct Mem *var)
 	} else if (var->flags & MEM_Int) {
 		i = var->u.i;
 encode_int:
-		if (var->u.i >= 0 || var->flags & MEM_Unsigned)
+		if (var->u.i >= 0)
 			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) {
-- 
2.17.1

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [tarantool-patches] Re: [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type
  2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
                   ` (14 preceding siblings ...)
  2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 15/15] " Stanislav Zudin
@ 2019-04-02 16:54 ` n.pettik
  15 siblings, 0 replies; 17+ messages in thread
From: n.pettik @ 2019-04-02 16:54 UTC (permalink / raw)
  To: tarantool-patches; +Cc: szudin



> On 1 Apr 2019, at 23:44, Stanislav Zudin <szudin@tarantool.org> wrote:
> 
> The patch enables support of big integers in the range [2^63, 2^64-1].
> Conversion functions use return value to inform about value
> they return - signed integer in the range [-2^63,2^63-1] or 
> unsigned integer in the range [2^63, 2^64 -1].
> The changes affect sql processing, VDBE, arithmetic functions and
> aggregate functions.
> 
> The second version includes the following:
> - New tests
> - VDBE treats unsigned as a separate type.
> - Refactored mapping vdbe data types to sql types
> - Fixed bugs in the previous commits
> - The type conversion supports unsigned integers
> 
> Issue: https://github.com/tarantool/tarantool/issues/3810
> Branch: https://github.com/tarantool/tarantool/tree/stanztt/gh-3810-sql-uint64-support
> 
> Stanislav Zudin (15):
>  sql: Convert big integers from string
>  sql: make VDBE recognize big integers
>  sql: removes unused function.
>  sql: support big integers within sql binding
>  sql: removes redundant function.
>  sql: arithmetic functions support big integers
>  sql: aggregate sql functions support big int
>  sql: fixes errors
>  sql: support -2^63 .. 2^64-1 integer type
>  sql: support -2^63 .. 2^64-1 integer type
>  sql: support -2^63 .. 2^64-1 integer type
>  sql: support -2^63 .. 2^64-1 integer type
>  sql: support -2^63 .. 2^64-1 integer type
>  sql: support -2^63 .. 2^64-1 integer type
>  sql: support -2^63 .. 2^64-1 integer type

Could you please organise your patch-set? Squash review fixes,
squash tests with corresponding patches, squash similar changes
etc.

Now I simply can’t navigate through these patches…

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2019-04-02 16:54 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-01 20:44 [tarantool-patches] [PATCH v2 00/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 01/15] sql: Convert big integers from string Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 02/15] sql: make VDBE recognize big integers Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 03/15] sql: removes unused function Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 04/15] sql: support big integers within sql binding Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 05/15] sql: removes redundant function Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 06/15] sql: arithmetic functions support big integers Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 07/15] sql: aggregate sql functions support big int Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 08/15] sql: fixes errors Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 09/15] sql: support -2^63 .. 2^64-1 integer type Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 10/15] " Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 11/15] " Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 12/15] " Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 13/15] " Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 14/15] " Stanislav Zudin
2019-04-01 20:44 ` [tarantool-patches] [PATCH v2 15/15] " Stanislav Zudin
2019-04-02 16:54 ` [tarantool-patches] Re: [PATCH v2 00/15] " n.pettik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox