From ce60e84bb84904f182c3cb60a03fe1b74c831b10 Mon Sep 17 00:00:00 2001
From: Konstantin Osipov <kostja@tarantool.org>
Date: Fri, 27 Feb 2015 16:43:42 +0300
Subject: [PATCH] gh-528 (support for arithmetic types in update): review fixes

- add comments
- minor renames (alex, please don't hate me)
- prepare for a split of make_arithmetic_operation
---
 src/box/errcode.h       |   2 +-
 src/box/tuple_update.cc | 303 +++++++++++++++++++++++-----------------
 test/box/update.result  |  30 ++--
 3 files changed, 188 insertions(+), 147 deletions(-)

diff --git a/src/box/errcode.h b/src/box/errcode.h
index 82260a44d1..29b950a262 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -146,7 +146,7 @@ struct errcode_record {
 	/* 92 */_(ER_ROLE_NOT_GRANTED,		2, "User '%s' does not have role '%s'") \
 	/* 93 */_(ER_MISSING_SNAPSHOT,		2, "Can't find snapshot") \
 	/* 94 */_(ER_CANT_UPDATE_PRIMARY_KEY,	2, "Attempt to modify a tuple field which is part of index %s") \
-	/* 95 */_(ER_UPDATE_INTEGER_OVERFLOW,   2, "Error: integer overflow during '%c' update of field %u") \
+	/* 95 */_(ER_UPDATE_INTEGER_OVERFLOW,   2, "Integer overflow when performing '%c' operation on field %u") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/tuple_update.cc b/src/box/tuple_update.cc
index 888a6590df..878b61a754 100644
--- a/src/box/tuple_update.cc
+++ b/src/box/tuple_update.cc
@@ -42,13 +42,13 @@
  * add or remove fields. Only one operation on the same field
  * is allowed.
  *
- * Supported field change operations are: SET, ADD, SUBSTRACT,
+ * Supported field change operations are: SET, ADD, SUBTRACT,
  * MULTIPLY; bitwise AND, XOR and OR; SPLICE.
  *
- * Supported tuple change operations are: SET (when SET field_no
- * == last_field_no + 1), DELETE, INSERT, PUSH and POP.
+ * Supported tuple change operations are: SET, DELETE, INSERT,
+ * PUSH and POP.
  * If the number of fields in a tuple is altered by an operation,
- * field index of all following operation is evaluated against the
+ * field index of all following operations is evaluated against the
  * new tuple.
  *
  * Despite the allowed complexity, a typical use case for UPDATE
@@ -84,7 +84,7 @@
  * In particular, if a field is deleted by an operation,
  * it disappears from the rope and all subsequent operations
  * on this field number instead affect the field following the
- * one.
+ * deleted one.
  */
 
 /** Update internal state */
@@ -104,21 +104,46 @@ struct op_set_arg {
 	const char *value;
 };
 
+/**
+ * MsgPack format code of an arithmetic argument or result.
+ * MsgPack codes are not used to simplify type calculation.
+ */
 enum arith_type {
-	AT_DOUBLE = 0,
-	AT_FLOAT = 1,
-	AT_NEGAT_INT = 2,
-	AT_POSIT_UINT = 3
+	AT_DOUBLE = 0, /* MP_DOUBLE */
+	AT_FLOAT = 1, /* MP_FLOAT */
+	AT_INT = 2, /* MP_INT */
+	AT_UINT = 3 /* MP_UINT */
 };
 
-/** Argument of ADD, AND, XOR, OR and etc operations. */
-struct op_arith_arg {
+/**
+ * Argument (left and right) and result of ADD, AND, XOR, OR,
+ * SUBTRACT, MULTIPLY.
+ *
+ * To perform an arithmetic operation, update first loads
+ * left and right arguments into corresponding value objects,
+ * then performs arithmetics on types of arguments, thus
+ * calculating the type of the result, and then
+ * performs the requested operation according to the calculated
+ * type rules.
+ *
+ * The rules are as follows:
+ * - when one of the argument types is double, the result is
+ *   double
+ * - when one of the argument types is float, the result is
+ *   float
+ * - for integer arguments, the result type code depends on
+ *   the range in which falls the result of the operation.
+ *   If the result is in negative range, it's MP_INT, otherwise
+ *   it's MP_UINT. If the result is out of bounds of (-2^63,
+ *   2^64), and exception is raised for overflow.
+ */
+struct arith_value {
 	enum arith_type type;
 	union {
-		uint64_t posit_uint;
-		int64_t negat_int;
-		double dbl;
-		float flt;
+		uint64_t v_uint64;
+		int64_t v_int64;
+		double v_double;
+		float v_float;
 	};
 };
 
@@ -137,7 +162,7 @@ struct op_splice_arg {
 
 union update_op_arg {
 	struct op_set_arg set;
-	struct op_arith_arg arith;
+	struct arith_value arith;
 	struct op_splice_arg splice;
 };
 
@@ -195,6 +220,7 @@ update_field_init(struct update_field *field,
 	field->tail_len = tail_len;
 }
 
+/** Read a field index or any other integer field. */
 static inline int64_t
 mp_read_int(struct tuple_update *update, struct update_op *op,
 	    const char **expr)
@@ -210,29 +236,33 @@ mp_read_int(struct tuple_update *update, struct update_op *op,
 	return field_no;
 }
 
-static inline struct op_arith_arg
-mp_read_arith_arg(struct tuple_update *update, struct update_op *op,
+/**
+ * Load an argument of an arithmetic operation either from tuple
+ * or from the UPDATE command.
+ */
+static inline struct arith_value
+mp_read_arith_value(struct tuple_update *update, struct update_op *op,
 		  const char **expr)
 {
-	struct op_arith_arg result;
+	struct arith_value result;
 	if (mp_typeof(**expr) == MP_UINT) {
-		result.type = AT_POSIT_UINT;
-		result.posit_uint = mp_decode_uint(expr);
+		result.type = AT_UINT;
+		result.v_uint64 = mp_decode_uint(expr);
 	} else if (mp_typeof(**expr) == MP_INT) {
 		int64_t val = mp_decode_int(expr);
 		if (val < 0) {
-			result.type = AT_NEGAT_INT;
-			result.negat_int = val;
+			result.type = AT_INT;
+			result.v_int64 = val;
 		} else {
-			result.type = AT_POSIT_UINT;
-			result.posit_uint = (uint64_t)val;
+			result.type = AT_UINT;
+			result.v_uint64 = (uint64_t) val;
 		}
 	} else if (mp_typeof(**expr) == MP_DOUBLE) {
 		result.type = AT_DOUBLE;
-		result.dbl = mp_decode_double(expr);
+		result.v_double = mp_decode_double(expr);
 	} else if (mp_typeof(**expr) == MP_FLOAT) {
 		result.type = AT_FLOAT;
-		result.flt = mp_decode_float(expr);
+		result.v_float = mp_decode_float(expr);
 	} else {
 		tnt_raise(ClientError, ER_ARG_TYPE, (char ) op->opcode,
 			  update->index_base + op->field_no, "NUMBER");
@@ -240,18 +270,23 @@ mp_read_arith_arg(struct tuple_update *update, struct update_op *op,
 	return result;
 }
 
+/** Return the MsgPack size of an arithmetic operation result. */
 static inline uint32_t
-mp_sizeof_arith_arg(struct op_arith_arg arg)
+mp_sizeof_arith_value(struct arith_value arg)
 {
-	if (arg.type == AT_POSIT_UINT) {
-		return mp_sizeof_uint(arg.posit_uint);
-	} else if (arg.type == AT_NEGAT_INT) {
-		return mp_sizeof_int(arg.negat_int);
+	/*
+	 * Arrange branches according to the likelihood of the
+	 * result type.
+	 */
+	if (arg.type == AT_UINT) {
+		return mp_sizeof_uint(arg.v_uint64);
+	} else if (arg.type == AT_INT) {
+		return mp_sizeof_int(arg.v_int64);
 	} else if (arg.type == AT_DOUBLE) {
-		return mp_sizeof_double(arg.dbl);
+		return mp_sizeof_double(arg.v_double);
 	} else {
 		assert(arg.type == AT_FLOAT);
-		return mp_sizeof_float(arg.flt);
+		return mp_sizeof_float(arg.v_float);
 	}
 }
 
@@ -342,35 +377,35 @@ do_op_delete(struct tuple_update *update, struct update_op *op,
 		rope_erase(update->rope, op->field_no);
 }
 
-static inline struct op_arith_arg
-make_arith_operation(struct op_arith_arg arg1, struct op_arith_arg arg2,
+static inline struct arith_value
+make_arith_operation(struct arith_value arg1, struct arith_value arg2,
 		     char opcode, uint32_t err_fieldno)
 {
-	struct op_arith_arg result;
+	struct arith_value result;
 
 	arith_type lowest_type = arg1.type;
 	if (arg1.type > arg2.type)
 		lowest_type = arg2.type;
 
-	if (lowest_type == AT_POSIT_UINT) {
+	if (lowest_type == AT_UINT) {
 		/* both operands are UINT */
-		result.type = AT_POSIT_UINT; /* could be overwritten below */
-		uint64_t a = arg1.posit_uint;
-		uint64_t b = arg2.posit_uint;
+		result.type = AT_UINT; /* could be overwritten below */
+		uint64_t a = arg1.v_uint64;
+		uint64_t b = arg2.v_uint64;
 		switch(opcode) {
 		case '+':
-			result.posit_uint = a + b;
-			if (result.posit_uint < a || result.posit_uint < b)
+			result.v_uint64 = a + b;
+			if (result.v_uint64 < a || result.v_uint64 < b)
 				tnt_raise(ClientError,
 					  ER_UPDATE_INTEGER_OVERFLOW,
 					  opcode, err_fieldno);
 			break;
 		case '-':
 			if (a >= b) {
-				result.posit_uint = a - b;
+				result.v_uint64 = a - b;
 			} else {
-				result.type = AT_NEGAT_INT;
-				result.negat_int = (int64_t)(a - b);
+				result.type = AT_INT;
+				result.v_int64 = (int64_t)(a - b);
 				if (b - a > -INT64_MIN)
 					tnt_raise(ClientError,
 						  ER_UPDATE_INTEGER_OVERFLOW,
@@ -378,7 +413,7 @@ make_arith_operation(struct op_arith_arg arg1, struct op_arith_arg arg2,
 			}
 			break;
 		case '*':
-			result.posit_uint = a * b;
+			result.v_uint64 = a * b;
 			{
 				uint64_t ah = a >> 32, al = a & 0xFFFFFFFF;
 				uint64_t bh = b >> 32, bl = b & 0xFFFFFFFF;
@@ -391,51 +426,51 @@ make_arith_operation(struct op_arith_arg arg1, struct op_arith_arg arg2,
 			}
 			break;
 		case '&':
-			result.posit_uint = a & b;
+			result.v_uint64 = a & b;
 			break;
 		case '|':
-			result.posit_uint = a | b;
+			result.v_uint64 = a | b;
 			break;
 		case '^':
-			result.posit_uint = a ^ b;
+			result.v_uint64 = a ^ b;
 			break;
 		default:
 			assert(false); /* checked by update_read_ops */
 			break;
 		}
-	} else if (lowest_type == AT_NEGAT_INT) {
+	} else if (lowest_type == AT_INT) {
 		/* both operands are integers; at least one - negative */
-		int test = ((int)arg2.type - (int)AT_NEGAT_INT) * 2
-			 + ((int)arg1.type - (int)AT_NEGAT_INT);
+		int test = ((int)arg2.type - (int)AT_INT) * 2
+			 + ((int)arg1.type - (int)AT_INT);
 		if (test == 2) {
 			/* first is negative, second is positive */
-			assert(arg1.type == AT_NEGAT_INT);
-			assert(arg2.type == AT_POSIT_UINT);
-			int64_t a = arg1.negat_int;
-			uint64_t b = arg2.posit_uint;
+			assert(arg1.type == AT_INT);
+			assert(arg2.type == AT_UINT);
+			int64_t a = arg1.v_int64;
+			uint64_t b = arg2.v_uint64;
 			uint64_t ma = (uint64_t)(-a); /* modulus a */
 			switch(opcode) {
 			case '+':
 				if (b >= ma) {
-					result.type = AT_POSIT_UINT;
-					result.posit_uint = b - ma;
+					result.type = AT_UINT;
+					result.v_uint64 = b - ma;
 				} else {
-					result.type = AT_NEGAT_INT;
-					result.negat_int = ((int64_t)b) + a;
+					result.type = AT_INT;
+					result.v_int64 = ((int64_t)b) + a;
 				}
 				break;
 			case '-':
-				result.type = AT_NEGAT_INT;
-				result.negat_int = a - b;
-				if (b >= -INT64_MIN || result.negat_int > a ||
-				    result.negat_int > -b)
+				result.type = AT_INT;
+				result.v_int64 = a - b;
+				if (b >= -INT64_MIN || result.v_int64 > a ||
+				    result.v_int64 > -b)
 					tnt_raise(ClientError,
 						  ER_UPDATE_INTEGER_OVERFLOW,
 						  opcode, err_fieldno);
 				break;
 			case '*':
-				result.type = AT_NEGAT_INT;
-				result.negat_int = a * (int64_t)b;
+				result.type = AT_INT;
+				result.v_int64 = a * (int64_t)b;
 				{
 					uint64_t ah = ma >> 32,
 						 al = ma & 0xFFFFFFFF;
@@ -450,9 +485,9 @@ make_arith_operation(struct op_arith_arg arg1, struct op_arith_arg arg2,
 							  ER_UPDATE_INTEGER_OVERFLOW,
 							  opcode, err_fieldno);
 				}
-				if (result.negat_int == 0) {
-					result.type = AT_POSIT_UINT;
-					result.posit_uint = 0;
+				if (result.v_int64 == 0) {
+					result.type = AT_UINT;
+					result.v_uint64 = 0;
 				}
 				break;
 			default:
@@ -462,33 +497,33 @@ make_arith_operation(struct op_arith_arg arg1, struct op_arith_arg arg2,
 			}
 		} else if (test == 1) {
 			/* first is positive, second is negative */
-			assert(arg1.type == AT_POSIT_UINT);
-			assert(arg2.type == AT_NEGAT_INT);
-			uint64_t a = arg1.posit_uint;
-			int64_t b = arg2.negat_int;
+			assert(arg1.type == AT_UINT);
+			assert(arg2.type == AT_INT);
+			uint64_t a = arg1.v_uint64;
+			int64_t b = arg2.v_int64;
 			uint64_t mb = (uint64_t)(-b); /* modulus b */
 			switch(opcode) {
 			case '+':
 				if (a >= mb) {
-					result.type = AT_POSIT_UINT;
-					result.posit_uint = a - mb;
+					result.type = AT_UINT;
+					result.v_uint64 = a - mb;
 				} else {
-					result.type = AT_NEGAT_INT;
-					result.negat_int = ((int64_t)a) + b;
+					result.type = AT_INT;
+					result.v_int64 = ((int64_t)a) + b;
 				}
 				break;
 			case '-':
-				result.type = AT_POSIT_UINT;
-				result.posit_uint = a + mb;
-				if (result.posit_uint < a ||
-				    result.posit_uint < mb)
+				result.type = AT_UINT;
+				result.v_uint64 = a + mb;
+				if (result.v_uint64 < a ||
+				    result.v_uint64 < mb)
 					tnt_raise(ClientError,
 						  ER_UPDATE_INTEGER_OVERFLOW,
 						  opcode, err_fieldno);
 				break;
 			case '*':
-				result.type = AT_NEGAT_INT;
-				result.negat_int = ((int64_t)a) * b;
+				result.type = AT_INT;
+				result.v_int64 = ((int64_t)a) * b;
 				{
 					uint64_t ah = a >> 32,
 						 al = a & 0xFFFFFFFF;
@@ -503,9 +538,9 @@ make_arith_operation(struct op_arith_arg arg1, struct op_arith_arg arg2,
 							  ER_UPDATE_INTEGER_OVERFLOW,
 							  opcode, err_fieldno);
 				}
-				if (result.negat_int == 0) {
-					result.type = AT_POSIT_UINT;
-					result.posit_uint = 0;
+				if (result.v_int64 == 0) {
+					result.type = AT_UINT;
+					result.v_uint64 = 0;
 				}
 				break;
 			default:
@@ -516,33 +551,33 @@ make_arith_operation(struct op_arith_arg arg1, struct op_arith_arg arg2,
 		} else {
 			/* both are negative */
 			assert(test == 0);
-			assert(arg1.type == AT_NEGAT_INT);
-			assert(arg2.type == AT_NEGAT_INT);
-			int64_t a = arg1.negat_int;
-			int64_t b = arg2.negat_int;
+			assert(arg1.type == AT_INT);
+			assert(arg2.type == AT_INT);
+			int64_t a = arg1.v_int64;
+			int64_t b = arg2.v_int64;
 			uint64_t ma = (uint64_t)(-a); /* modulus a */
 			uint64_t mb = (uint64_t)(-b); /* modulus b */
 			switch(opcode) {
 			case '+':
-				result.type = AT_NEGAT_INT;
-				result.negat_int = a + b;
-				if (result.negat_int > a || result.negat_int > b)
+				result.type = AT_INT;
+				result.v_int64 = a + b;
+				if (result.v_int64 > a || result.v_int64 > b)
 					tnt_raise(ClientError,
 						  ER_UPDATE_INTEGER_OVERFLOW,
 						  opcode, err_fieldno);
 				break;
 			case '-':
 				if (a >= b) {
-					result.type = AT_POSIT_UINT;
-					result.posit_uint = (uint64_t)(a - b);
+					result.type = AT_UINT;
+					result.v_uint64 = (uint64_t)(a - b);
 				} else {
-					result.type = AT_NEGAT_INT;
-					result.negat_int = a - b;
+					result.type = AT_INT;
+					result.v_int64 = a - b;
 				}
 				break;
 			case '*':
-				result.type = AT_POSIT_UINT;
-				result.posit_uint = ma * mb;
+				result.type = AT_UINT;
+				result.v_uint64 = ma * mb;
 				{
 					uint64_t ah = ma >> 32,
 						 al = ma & 0xFFFFFFFF;
@@ -566,25 +601,25 @@ make_arith_operation(struct op_arith_arg arg1, struct op_arith_arg arg2,
 		/* At least one of operands is double or float */
 		double a;
 		if (arg1.type == AT_DOUBLE) {
-			a = arg1.dbl;
+			a = arg1.v_double;
 		} else if (arg1.type == AT_FLOAT) {
-			a = arg1.flt;
-		} else if (arg1.type == AT_POSIT_UINT) {
-			a = arg1.posit_uint;
+			a = arg1.v_float;
+		} else if (arg1.type == AT_UINT) {
+			a = arg1.v_uint64;
 		} else {
-			assert(arg1.type == AT_NEGAT_INT);
-			a = arg1.negat_int;
+			assert(arg1.type == AT_INT);
+			a = arg1.v_int64;
 		}
 		double b;
 		if (arg2.type == AT_DOUBLE) {
-			b = arg2.dbl;
+			b = arg2.v_double;
 		} else if (arg2.type == AT_FLOAT) {
-			b = arg2.flt;
-		} else if (arg2.type == AT_POSIT_UINT) {
-			b = arg2.posit_uint;
+			b = arg2.v_float;
+		} else if (arg2.type == AT_UINT) {
+			b = arg2.v_uint64;
 		} else {
-			assert(arg2.type == AT_NEGAT_INT);
-			b = arg2.negat_int;
+			assert(arg2.type == AT_INT);
+			b = arg2.v_int64;
 		}
 		double c;
 		switch(opcode) {
@@ -599,38 +634,44 @@ make_arith_operation(struct op_arith_arg arg1, struct op_arith_arg arg2,
 		if (lowest_type == AT_DOUBLE) {
 			/* result is DOUBLE */
 			result.type = AT_DOUBLE;
-			result.dbl = c;
+			result.v_double = c;
 		} else {
 			/* result is FLOAT */
 			assert(lowest_type == AT_FLOAT);
 			result.type = AT_FLOAT;
-			result.flt = (float)c;
+			result.v_float = (float)c;
 		}
 	}
 	return result;
 }
 
 static void
-do_op_arith(struct tuple_update *update, struct update_op *op,
-	    const char **expr)
+prepare_op_arith(struct tuple_update *update, struct update_op *op,
+		 const char **expr, struct arith_value *left)
 {
 	op_check_field_no(update, op->field_no, rope_size(update->rope) - 1);
 
 	struct update_field *field = (struct update_field *)
 			rope_extract(update->rope, op->field_no);
-
-	struct op_arith_arg *result = &op->arg.arith;
-	*result = mp_read_arith_arg(update, op, expr);
 	if (field->op) {
 		tnt_raise(ClientError, ER_UPDATE_FIELD, update->index_base +
 			  op->field_no, "double update of the same field");
 	}
 	field->op = op;
 	const char *old = field->old;
-	struct op_arith_arg first = mp_read_arith_arg(update, op, &old);
-	*result = make_arith_operation(first, *result, op->opcode,
-				       update->index_base + op->field_no);
-	op->new_field_len = mp_sizeof_arith_arg(*result);
+	*left = mp_read_arith_value(update, op, &old);
+	op->arg.arith = mp_read_arith_value(update, op, expr);
+}
+
+static void
+do_op_arith(struct tuple_update *update, struct update_op *op,
+	    const char **expr)
+{
+	struct arith_value left;
+	prepare_op_arith(update, op, expr, &left);
+	op->arg.arith = make_arith_operation(left, op->arg.arith, op->opcode,
+					     update->index_base + op->field_no);
+	op->new_field_len = mp_sizeof_arith_value(op->arg.arith);
 }
 
 static void
@@ -703,18 +744,18 @@ store_op_set(struct op_set_arg *arg, const char *in __attribute__((unused)),
 }
 
 static void
-store_op_arith(struct op_arith_arg *arg, const char *in __attribute__((unused)), char *out)
+store_op_arith(struct arith_value *arg, const char *in __attribute__((unused)), char *out)
 {
-	if (arg->type == AT_POSIT_UINT) {
-		mp_encode_uint(out, arg->posit_uint);
-	} else if (arg->type == AT_NEGAT_INT) {
-		assert(arg->negat_int < 0);
-		mp_encode_int(out, arg->negat_int);
+	if (arg->type == AT_UINT) {
+		mp_encode_uint(out, arg->v_uint64);
+	} else if (arg->type == AT_INT) {
+		assert(arg->v_int64 < 0);
+		mp_encode_int(out, arg->v_int64);
 	} else if (arg->type == AT_DOUBLE) {
-		mp_encode_double(out, arg->dbl);
+		mp_encode_double(out, arg->v_double);
 	} else {
 		assert(arg->type == AT_FLOAT);
-		mp_encode_float(out, arg->flt);
+		mp_encode_float(out, arg->v_float);
 	}
 }
 
diff --git a/test/box/update.result b/test/box/update.result
index 684ef66305..2b16d6bca2 100644
--- a/test/box/update.result
+++ b/test/box/update.result
@@ -609,11 +609,11 @@ s:update({0}, {{'+', 2, 1}}) -- ok
 ...
 s:update({0}, {{'+', 2, 1}}) -- overflow
 ---
-- error: 'Error: integer overflow during ''+'' update of field 2'
+- error: Integer overflow when performing '+' operation on field 2
 ...
 s:update({0}, {{'+', 2, 100500}}) -- overflow
 ---
-- error: 'Error: integer overflow during ''+'' update of field 2'
+- error: Integer overflow when performing '+' operation on field 2
 ...
 s:replace{0, 1}
 ---
@@ -621,7 +621,7 @@ s:replace{0, 1}
 ...
 s:update({0}, {{'+', 2, 0xffffffffffffffffull}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''+'' update of field 2'
+- error: Integer overflow when performing '+' operation on field 2
 ...
 s:replace{0, 0xffffffff}
 ---
@@ -633,7 +633,7 @@ s:update({0}, {{'*', 2, 0xffffffff}})  -- ok
 ...
 s:update({0}, {{'*', 2, 2}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''*'' update of field 2'
+- error: Integer overflow when performing '*' operation on field 2
 ...
 s:replace{0, 0xffffffff}
 ---
@@ -649,7 +649,7 @@ s:replace{0, 0xffffffff}
 ...
 s:update({0}, {{'*', 2, 0x100000002ull}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''*'' update of field 2'
+- error: Integer overflow when performing '*' operation on field 2
 ...
 s:replace{0, 0x100000000ull}
 ---
@@ -657,7 +657,7 @@ s:replace{0, 0x100000000ull}
 ...
 s:update({0}, {{'*', 2, 0x100000000ull}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''*'' update of field 2'
+- error: Integer overflow when performing '*' operation on field 2
 ...
 s:replace{0, -1}
 ---
@@ -685,7 +685,7 @@ s:update({0}, {{'-', 2, 0x7fffffffffffffffull}})  -- ok
 ...
 s:update({0}, {{'*', 2, 2}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''*'' update of field 2'
+- error: Integer overflow when performing '*' operation on field 2
 ...
 s:replace{0, -2}
 ---
@@ -693,7 +693,7 @@ s:replace{0, -2}
 ...
 s:update({0}, {{'-', 2, 0x7fffffffffffffffull}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''-'' update of field 2'
+- error: Integer overflow when performing '-' operation on field 2
 ...
 s:replace{0, 1}
 ---
@@ -701,7 +701,7 @@ s:replace{0, 1}
 ...
 s:update({0}, {{'-', 2, 0xffffffffffffffffull}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''-'' update of field 2'
+- error: Integer overflow when performing '-' operation on field 2
 ...
 s:replace{0, 0xffffffffffffffefull}
 ---
@@ -713,7 +713,7 @@ s:update({0}, {{'-', 2, -16}})  -- ok
 ...
 s:update({0}, {{'-', 2, -16}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''-'' update of field 2'
+- error: Integer overflow when performing '-' operation on field 2
 ...
 s:replace{0, 0x4000000000000000ull}
 ---
@@ -725,7 +725,7 @@ s:update({0}, {{'*', 2, -2}})  -- ok
 ...
 s:update({0}, {{'*', 2, -2}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''*'' update of field 2'
+- error: Integer overflow when performing '*' operation on field 2
 ...
 s:replace{0, 0x4000000000000001ull}
 ---
@@ -733,7 +733,7 @@ s:replace{0, 0x4000000000000001ull}
 ...
 s:update({0}, {{'*', 2, -2}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''*'' update of field 2'
+- error: Integer overflow when performing '*' operation on field 2
 ...
 s:replace{0, -0x4000000000000000ll}
 ---
@@ -749,7 +749,7 @@ s:replace{0, -0x4000000000000000ll}
 ...
 s:update({0}, {{'+', 2, -0x4000000000000001ll}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''+'' update of field 2'
+- error: Integer overflow when performing '+' operation on field 2
 ...
 s:replace{0, -0xffffffffll}
 ---
@@ -765,7 +765,7 @@ s:replace{0, 0xffffffff}
 ...
 s:update({0}, {{'*', 2, -0x100000002ll}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''*'' update of field 2'
+- error: Integer overflow when performing '*' operation on field 2
 ...
 s:replace{0, 0x100000000ull}
 ---
@@ -773,7 +773,7 @@ s:replace{0, 0x100000000ull}
 ...
 s:update({0}, {{'*', 2, -0x100000000ll}})  -- overflow
 ---
-- error: 'Error: integer overflow during ''*'' update of field 2'
+- error: Integer overflow when performing '*' operation on field 2
 ...
 s:drop()
 ---
-- 
GitLab