[tarantool-patches] [PATCH v2 12/15] sql: support -2^63 .. 2^64-1 integer type

Stanislav Zudin szudin at tarantool.org
Mon Apr 1 23:44:50 MSK 2019


Adds functions setting and retrieving unsigned value.
Adds a new member to struct sql_bind and struct Mem
to keep an unsigned value
atoi functions return 0 on success and -1 on error
and return the signed/unsigned property by pointer.
The cast operations take the unsigned into account.
Fixes the affected tests.
The sql function avg() uses uint64.

Part of #3810
---
 src/box/execute.c                |  9 ++--
 src/box/lua/lua_sql.c            |  2 +-
 src/box/lua/sql.c                |  2 +-
 src/box/sql/expr.c               | 30 +++++++------
 src/box/sql/func.c               | 38 +++++++++++------
 src/box/sql/sqlInt.h             | 63 +++++++++++----------------
 src/box/sql/util.c               | 72 ++++++++++++++-----------------
 src/box/sql/vdbe.c               | 30 +++++++------
 src/box/sql/vdbeInt.h            |  4 +-
 src/box/sql/vdbeapi.c            | 30 ++++++++++---
 src/box/sql/vdbemem.c            | 73 +++++++++++++++++++++-----------
 test/sql-tap/func.test.lua       |  2 +-
 test/sql/integer-overflow.result |  4 +-
 13 files changed, 201 insertions(+), 158 deletions(-)

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





More information about the Tarantool-patches mailing list