Tarantool development patches archive
 help / color / mirror / Atom feed
From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
To: imeevma@tarantool.org, tsafin@tarantool.org,
	tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH v1 1/1] sql: remove implicit cast for COMPARISON
Date: Mon, 30 Mar 2020 00:29:22 +0200	[thread overview]
Message-ID: <876090db-cb61-0c46-427a-d5a856fad4f1@tarantool.org> (raw)
In-Reply-To: <9a402676dbab6bdc4b1cbbdfc1f5069214fdfa06.1584707598.git.imeevma@gmail.com>

Hi! Thanks for the patch!

See 15 comments below.

I don't think I understood all the changed (because I am very out of
context), so I will revisited many of them when you send v2.

On 20/03/2020 13:34, imeevma@tarantool.org wrote:
> This patch removes implicit cast for comparison. Also, it make
> search using an index work the same way as in case of fullscan in
> most cases.
> 
> Closes #4230
> Closes #4783
> ---
> https://github.com/tarantool/tarantool/issues/4230
> https://github.com/tarantool/tarantool/issues/4783
> https://github.com/tarantool/tarantool/tree/imeevma/gh-4230-remove-implicit-cast-for-index-search-second
> 
> @ChangeLog Remove implicit cast for comparison gh-4230

1. Worth adding more details. What implicit cast? All of them?

Do we need a docbot request for this?

> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> index 1579cc9..cd626bd 100644
> --- a/src/box/sql/sqlInt.h
> +++ b/src/box/sql/sqlInt.h
> @@ -1304,6 +1304,9 @@ enum trim_side_mask {
>  				 (X) == FIELD_TYPE_UNSIGNED || \
>  				 (X) == FIELD_TYPE_DOUBLE)
>  
> +#define sql_mp_type_is_numeric(X)  ((X) == MP_INT || (X) == MP_UINT || \
> +				    (X) == MP_FLOAT || (X) == MP_DOUBLE)

2. Better make it static inline function.

> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> index e8a029a..8c95fd4 100644
> --- a/src/box/sql/vdbe.c
> +++ b/src/box/sql/vdbe.c
> @@ -660,6 +660,216 @@ vdbe_field_ref_fetch_field(struct vdbe_field_ref *field_ref, uint32_t fieldno)
>  }
>  
>  /**
> + * Prepare mem to use for comparison with value with type UNSIGNED
> + * from index.
> + *
> + * @param[out] mem Mem to prepare for comparison.
> + * @param[in][out] op Operation that was before preperation and
> + *                 operation after.

3. preperation -> preparation. The same in the next functions.

> + * @param is_eq True if operation was EQ.
> + * @param[out] is_none True if nothing will be found.
> + * @retval 0 on success.
> + * @retval -1 on error.
> + */
> +static int
> +sql_mem_to_unsigned_for_comp(struct Mem *mem, uint32_t *op, int is_eq,

4. Opcode has type u8 in UnpackedRecord. Better make it the
same here. The same in other places.

5. is_eq is a flag, and therefore should be a boolean. The
same in the next functions.

> +			     bool *is_none)
> +{
> +	assert(*is_none == false);> +	uint32_t orig_op = *op;
> +	enum mp_type mp_type = sql_value_type(mem);
> +	assert(mp_type == MP_DOUBLE || mp_type == MP_INT);
> +	if (mp_type == MP_INT || mem->u.r < 0) {
> +		if (orig_op == OP_SeekGE || orig_op == OP_SeekGT) {
> +			mem_set_u64(mem, 0);
> +			*op = OP_SeekGE;
> +			return 0;
> +		}
> +		*is_none = true;
> +		return 0;
> +	}
> +	double d = mem->u.r;
> +	if (d >= UINT64_MAX) {
> +		if (orig_op == OP_SeekLE || orig_op == OP_SeekLT) {
> +			mem_set_u64(mem, UINT64_MAX);
> +			*op = OP_SeekLE;
> +			return 0;
> +		}
> +		*is_none = true;
> +		return 0;
> +	}
> +	uint64_t u = (uint64_t)d;
> +	if (d != u && is_eq) {
> +		*is_none = true;
> +		return 0;
> +	}
> +	mem_set_u64(mem, u);
> +	if (d == u)
> +		return 0;
> +	if (orig_op == OP_SeekGE)
> +		*op = OP_SeekGT;
> +	else if (orig_op == OP_SeekLT)
> +		*op = OP_SeekLE;
> +	return 0;
> +}
> +
> +/**
> + * Prepare mem to use for comparison with value with type INTEGER
> + * from index.
> + *
> + * @param[out] mem Mem to prepare for comparison.
> + * @param[in][out] op Operation that was before preperation and
> + *                 operation after.
> + * @param is_eq True if operation was EQ.
> + * @param[out] is_none True if nothing will be found.
> + * @retval 0 on success.
> + * @retval -1 on error.
> + */
> +static int
> +sql_mem_to_integer_for_comp(struct Mem *mem, uint32_t *op, int is_eq,
> +			    bool *is_none)
> +{
> +	assert(*is_none == false);
> +	uint32_t orig_op = *op;
> +	assert(sql_value_type(mem) == MP_DOUBLE);
> +	double d = mem->u.r;
> +	if (d < INT64_MIN) {
> +		if (orig_op == OP_SeekGE || orig_op == OP_SeekGT) {
> +			mem_set_i64(mem, INT64_MIN);
> +			*op = OP_SeekGE;
> +			return 0;
> +		}
> +		*is_none = true;
> +		return 0;
> +	}
> +	if (d >= UINT64_MAX) {
> +		if (orig_op == OP_SeekLE || orig_op == OP_SeekLT) {
> +			mem_set_u64(mem, UINT64_MAX);
> +			*op = OP_SeekLE;
> +			return 0;
> +		}
> +		*is_none = true;
> +		return 0;
> +	}
> +	int64_t i = (int64_t)d;
> +	uint64_t u = (uint64_t)d;
> +	if (d != i && d != u && is_eq) {
> +		*is_none = true;
> +		return 0;
> +	}
> +	if (d > 0)

6. >= 0?

> +		mem_set_u64(mem, u);
> +	else
> +		mem_set_i64(mem, i);
> +	if (d == u || d == i)
> +		return 0;
> +	if (d >= 0) {
> +		if (orig_op == OP_SeekGE)
> +			*op = OP_SeekGT;
> +		else if (orig_op == OP_SeekLT)
> +			*op = OP_SeekLE;
> +		return 0;
> +	}
> +	if (orig_op == OP_SeekLE)
> +		*op = OP_SeekLT;
> +	else if (orig_op == OP_SeekGT)
> +		*op = OP_SeekGE;
> +	return 0;
> +}
> +
> +/**
> + * Prepare mem to use for comparison with value with type DOUBLE
> + * from index.
> + *
> + * @param[out] mem Mem to prepare for comparison.
> + * @param[in][out] op Operation that was before preperation and
> + *                 operation after.
> + * @param is_eq True if operation was EQ.
> + * @param[out] is_none True if nothing will be found.
> + * @retval 0 on success.
> + * @retval -1 on error.
> + */
> +static int
> +sql_mem_to_double_for_comp(struct Mem *mem, uint32_t *op, int is_eq,
> +			   bool *is_none)
> +{
> +	assert(*is_none == false);
> +	uint32_t orig_op = *op;
> +	enum mp_type mp_type = sql_value_type(mem);

7. Is there a ticket to stop conveting MEM_* to MP_*? MP_* now
contains all MEM_* and even more, right?

> +	assert(mp_type == MP_INT || mp_type == MP_UINT);
> +	int64_t i = mem->u.i;
> +	uint64_t u = mem->u.u;
> +	/* 2^53 == 9007199254740992 */
> +	if (mp_type == MP_INT && i > -9007199254740992) {

8. Is it possible to replace this with 'i == (int64)(double)i' ?
The same below. Because these double vs int are always tricky.
And I would like to avoid any constant assumptions here. For
example, you excluded 2^53, but 2^53 can be stored (from what I
saw and tested). So it would be more correct to write >= -9007199254740992,
not >. However the 2-cast solution may work too, and does not
use constants.

> +		sqlVdbeMemSetDouble(mem, i);
> +		return 0;
> +	} else if (mp_type == MP_UINT && u < 9007199254740992) {
> +		sqlVdbeMemSetDouble(mem, u);
> +		return 0;
> +	}
> +
> +	double d = mp_type == MP_INT ? (double)i : (double)u;
> +	if (is_eq) {
> +		if (i != d && u != d)
> +			*is_none = true;
> +		else
> +			sqlVdbeMemSetDouble(mem, d);
> +		return 0;
> +	}
> +
> +	sqlVdbeMemSetDouble(mem, d);
> +	if (mp_type == MP_INT) {
> +		if (orig_op == OP_SeekGT && d > i)
> +			*op = OP_SeekGE;
> +		else if (orig_op == OP_SeekGE && d < i)
> +			*op = OP_SeekGT;
> +		else if (orig_op == OP_SeekLT && d < i)
> +			*op = OP_SeekLE;
> +		else if (orig_op == OP_SeekLE && d > i)
> +			*op = OP_SeekLT;
> +		return 0;
> +	}
> +	if (orig_op == OP_SeekGT && d > u)
> +		*op = OP_SeekGE;
> +	else if (orig_op == OP_SeekGE && d < u)
> +		*op = OP_SeekGT;
> +	else if (orig_op == OP_SeekLT && d < u)
> +		*op = OP_SeekLE;
> +	else if (orig_op == OP_SeekLE && d > u)
> +		*op = OP_SeekLT;
> +	return 0;
> +}
> +
> +/**
> + * Prepare mem to use for comparison with value with numeric type

9. No mem among the arguments.

> + * from index.
> + *
> + * @param[in][out] r Unpacked tuple that should be prepared for
> + *                 comparison.
> + * @param i field of unpacked tuple that should be prepared.
> + * @param is_eq True if operation was EQ.
> + * @param[out] is_none True if nothing will be found.
> + * @retval 0 on success.
> + * @retval -1 on error.
> + */
> +static int
> +sql_prepare_rec_for_comp(struct UnpackedRecord *r, int i, int is_eq,
> +			 bool *is_none)
> +{
> +	struct Mem *mem = &r->aMem[i];
> +	enum field_type field_type = r->key_def->parts[i].type;
> +	uint32_t opcode = r->opcode;
> +	if (field_type == FIELD_TYPE_UNSIGNED)
> +		sql_mem_to_unsigned_for_comp(mem, &opcode, is_eq, is_none);
> +	else if (field_type == FIELD_TYPE_INTEGER)
> +		sql_mem_to_integer_for_comp(mem, &opcode, is_eq, is_none);
> +	else
> +		sql_mem_to_double_for_comp(mem, &opcode, is_eq, is_none);
> +	r->opcode = opcode;
> +	return 0;
> +}
> @@ -2133,189 +2336,109 @@ case OP_Lt:               /* same as TK_LT, jump, in1, in3 */
>  case OP_Le:               /* same as TK_LE, jump, in1, in3 */
>  case OP_Gt:               /* same as TK_GT, jump, in1, in3 */
>  case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
> -	int res, res2;      /* Result of the comparison of pIn1 against pIn3 */
> -	u32 flags1;         /* Copy of initial value of pIn1->flags */
> -	u32 flags3;         /* Copy of initial value of pIn3->flags */
> -
> +	/*
> +	 * Result of comparison: more than 0 if l > r,
> +	 * 0 if l == r, less than 0 if l < r.
> +	 */
> +	int cmp_res;
> +	/* Result of the operation. */
> +	bool result;
>  	pIn1 = &aMem[pOp->p1];
>  	pIn3 = &aMem[pOp->p3];
> -	flags1 = pIn1->flags;
> -	flags3 = pIn3->flags;
> -	enum field_type ft_p1 = pIn1->field_type;
> -	enum field_type ft_p3 = pIn3->field_type;
> -	if ((flags1 | flags3)&MEM_Null) {
> -		/* One or both operands are NULL */
> -		if (pOp->p5 & SQL_NULLEQ) {
> -			/* If SQL_NULLEQ is set (which will only happen if the operator is
> -			 * OP_Eq or OP_Ne) then take the jump or not depending on whether
> -			 * or not both operands are null.
> -			 */
> -			assert(pOp->opcode==OP_Eq || pOp->opcode==OP_Ne);
> -			assert((flags1 & MEM_Cleared)==0);
> -			assert((pOp->p5 & SQL_JUMPIFNULL)==0);
> -			if ((flags1&flags3&MEM_Null)!=0
> -			    && (flags3&MEM_Cleared)==0
> -				) {
> -				res = 0;  /* Operands are equal */
> -			} else {
> -				res = 1;  /* Operands are not equal */
> -			}
> -		} else {
> -			/* SQL_NULLEQ is clear and at least one operand is NULL,
> -			 * then the result is always NULL.
> -			 * The jump is taken if the SQL_JUMPIFNULL bit is set.
> +
> +	bool has_scalar = (pIn1->field_type == FIELD_TYPE_SCALAR) ||
> +			  (pIn3->field_type == FIELD_TYPE_SCALAR);
> +	enum mp_type ltype = sql_value_type(pIn1);
> +	enum mp_type rtype = sql_value_type(pIn3);
> +	assert(ltype != MP_MAP && rtype != MP_MAP);
> +	assert(ltype != MP_ARRAY && rtype != MP_ARRAY);
> +	int mp_classes_comp = mp_type_classes_comp(rtype, ltype);
> +
> +	if (ltype == MP_NIL || rtype == MP_NIL) {
> +		if ((pOp->p5 & SQL_NULLEQ) == 0) {
> +			/*
> +			 * SQL_NULLEQ is clear and at least one
> +			 * operand is NULL, then the result is
> +			 * always NULL. The jump is taken if the
> +			 * SQL_JUMPIFNULL bit is set.
>  			 */
>  			if (pOp->p5 & SQL_STOREP2) {
>  				pOut = vdbe_prepare_null_out(p, pOp->p2);
> -				iCompare = 1;    /* Operands are not equal */
> +				iCompare = 1;
>  				REGISTER_TRACE(p, pOp->p2, pOut);
>  			} else {
>  				VdbeBranchTaken(2,3);
> -				if (pOp->p5 & SQL_JUMPIFNULL) {
> +				if (pOp->p5 & SQL_JUMPIFNULL)
>  					goto jump_to_p2;
> -				}

10. I know these places violated our style, but lets keep
them. Since they are not stictly related to the patch. We will
have a chance to fix them later.

>  			}
>  			break;
>  		}
> -	} else if (((flags1 | flags3) & MEM_Bool) != 0 ||
> -		   ((flags1 | flags3) & MEM_Blob) != 0) {
>  		/*
> -		 * If one of values is of type BOOLEAN or VARBINARY,
> -		 * then the second one must be of the same type as
> -		 * well. Otherwise an error is raised.
> +		 * If SQL_NULLEQ is set (which will only happen if
> +		 * the operator is OP_Eq or OP_Ne) then take the
> +		 * jump or not depending on whether or not both
> +		 * operands are null.
>  		 */
> -		int type_arg1 = flags1 & (MEM_Bool | MEM_Blob);
> -		int type_arg3 = flags3 & (MEM_Bool | MEM_Blob);
> -		if (type_arg1 != type_arg3) {
> -			char *inconsistent_type = type_arg1 != 0 ?
> -						  mem_type_to_str(pIn3) :
> -						  mem_type_to_str(pIn1);
> -			char *expected_type     = type_arg1 != 0 ?
> -						  mem_type_to_str(pIn1) :
> -						  mem_type_to_str(pIn3);
> -			diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
> -				 inconsistent_type, expected_type);
> -			goto abort_due_to_error;
> -		}
> -		res = sqlMemCompare(pIn3, pIn1, NULL);
> +		assert(pOp->opcode == OP_Eq || pOp->opcode == OP_Ne);
> +		assert((pOp->p5 & SQL_JUMPIFNULL) == 0);
> +		cmp_res = !(ltype == MP_NIL && rtype == MP_NIL);

11. The compiler is likely to help us here, but still lets apply '!'.
It will look

    ltype != MP_NIL || rtype != MP_NIL

> +	} else if (mp_classes_comp == 0) {
> +		cmp_res = sqlMemCompare(pIn3, pIn1, pOp->p4.pColl);
> +	} else if (has_scalar) {
> +		cmp_res = mp_classes_comp;
>  	} else {
> -		enum field_type type = pOp->p5 & FIELD_TYPE_MASK;
> -		if (sql_type_is_numeric(type)) {
> -			if ((flags1 | flags3)&MEM_Str) {
> -				if ((flags1 & MEM_Str) == MEM_Str) {
> -					mem_apply_numeric_type(pIn1);
> -					testcase( flags3!=pIn3->flags); /* Possible if pIn1==pIn3 */
> -					flags3 = pIn3->flags;
> -				}
> -				if ((flags3 & MEM_Str) == MEM_Str) {
> -					if (mem_apply_numeric_type(pIn3) != 0) {
> -						diag_set(ClientError,
> -							 ER_SQL_TYPE_MISMATCH,
> -							 sql_value_to_diag_str(pIn3),
> -							 "numeric");
> -						goto abort_due_to_error;
> -					}
> +		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
> +			 mem_type_to_str(pIn1), mem_type_to_str(pIn3));
> +		goto abort_due_to_error;
> +	}
>  
> -				}
> -			}
> -			/* Handle the common case of integer comparison here, as an
> -			 * optimization, to avoid a call to sqlMemCompare()
> -			 */
> -			if ((pIn1->flags & pIn3->flags & (MEM_Int | MEM_UInt)) != 0) {
> -				if ((pIn1->flags & pIn3->flags & MEM_Int) != 0) {
> -					if (pIn3->u.i > pIn1->u.i)
> -						res = +1;
> -					else if (pIn3->u.i < pIn1->u.i)
> -						res = -1;
> -					else
> -						res = 0;
> -					goto compare_op;
> -				}
> -				if ((pIn1->flags & pIn3->flags & MEM_UInt) != 0) {
> -					if (pIn3->u.u > pIn1->u.u)
> -						res = +1;
> -					else if (pIn3->u.u < pIn1->u.u)
> -						res = -1;
> -					else
> -						res = 0;
> -					goto compare_op;
> -				}
> -				if ((pIn1->flags & MEM_UInt) != 0 &&
> -				    (pIn3->flags & MEM_Int) != 0) {
> -					res = -1;
> -					goto compare_op;
> -				}
> -				res = 1;
> -				goto compare_op;
> -			}
> -		} else if (type == FIELD_TYPE_STRING) {
> -			if ((flags1 & MEM_Str) == 0 &&
> -			    (flags1 & (MEM_Int | MEM_UInt | MEM_Real)) != 0) {
> -				testcase( pIn1->flags & MEM_Int);
> -				testcase( pIn1->flags & MEM_Real);
> -				sqlVdbeMemStringify(pIn1);
> -				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_UInt | MEM_Real)) != 0) {
> -				testcase( pIn3->flags & MEM_Int);
> -				testcase( pIn3->flags & MEM_Real);
> -				sqlVdbeMemStringify(pIn3);
> -				testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn));
> -				flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask);
> -			}
> -		}
> -		assert(pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0);
> -		res = sqlMemCompare(pIn3, pIn1, pOp->p4.pColl);
> -	}
> -			compare_op:
> -	switch( pOp->opcode) {
> -	case OP_Eq:    res2 = res==0;     break;
> -	case OP_Ne:    res2 = res;        break;
> -	case OP_Lt:    res2 = res<0;      break;
> -	case OP_Le:    res2 = res<=0;     break;
> -	case OP_Gt:    res2 = res>0;      break;
> -	default:       res2 = res>=0;     break;
> -	}
> -
> -	/* Undo any changes made by mem_apply_type() to the input registers. */
> -	assert((pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn));
> -	pIn1->flags = flags1;
> -	pIn1->field_type = ft_p1;
> -	assert((pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn));
> -	pIn3->flags = flags3;
> -	pIn3->field_type = ft_p3;
> +	switch (pOp->opcode) {
> +	case OP_Eq:
> +		result = cmp_res == 0;
> +		break;
> +	case OP_Ne:
> +		result = cmp_res != 0;
> +		break;
> +	case OP_Lt:
> +		result = cmp_res < 0;
> +		break;
> +	case OP_Le:
> +		result = cmp_res <= 0;
> +		break;
> +	case OP_Gt:
> +		result = cmp_res > 0;
> +		break;
> +	case OP_Ge:
> +		result = cmp_res >= 0;
> +		break;
> +	default:
> +		unreachable();
> +	}
>  
>  	if (pOp->p5 & SQL_STOREP2) {
> -		iCompare = res;
> -		res2 = res2!=0;  /* For this path res2 must be exactly 0 or 1 */
> -		if ((pOp->p5 & SQL_KEEPNULL)!=0) {
> -			/* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with true
> -			 * and prevents OP_Ne from overwriting NULL with false.  This flag
> -			 * is only used in contexts where either:
> -			 *   (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0)
> -			 *   (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1)
> -			 * Therefore it is not necessary to check the content of r[P2] for
> -			 * NULL.
> -			 */
> -			assert(pOp->opcode==OP_Ne || pOp->opcode==OP_Eq);
> -			assert(res2==0 || res2==1);
> -			testcase( res2==0 && pOp->opcode==OP_Eq);
> -			testcase( res2==1 && pOp->opcode==OP_Eq);
> -			testcase( res2==0 && pOp->opcode==OP_Ne);
> -			testcase( res2==1 && pOp->opcode==OP_Ne);
> -			if ((pOp->opcode==OP_Eq)==res2) break;
> +		iCompare = cmp_res;
> +		/*
> +		 * The KEEPNULL flag prevents OP_Eq from
> +		 * overwriting a NULL with true and prevents OP_Ne
> +		 * from overwriting NULL with false. This flag
> +		 * is only used in contexts where either:
> +		 *   (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0)
> +		 *   (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1)
> +		 * Therefore it is not necessary to check the
> +		 * content of r[P2] for NULL.
> +		 */
> +		if ((pOp->p5 & SQL_KEEPNULL) != 0) {

12. This condition and the comment clearly could be kept as is, since
only formatting is changed. Could you please preserve the old lines?
For the sake of git blame. Also check other places where it is possible
to keep the old lines for free (without necessity to make new code
worse).

> +			assert(pOp->opcode == OP_Ne || pOp->opcode == OP_Eq);
> +			if ((pOp->opcode == OP_Eq) == result)
> +				break;
>  		}
>  		pOut = vdbe_prepare_null_out(p, pOp->p2);
> -		mem_set_bool(pOut, res2);
> +		mem_set_bool(pOut, result);
>  		REGISTER_TRACE(p, pOp->p2, pOut);
>  	} else {
> -		VdbeBranchTaken(res!=0, (pOp->p5 & SQL_NULLEQ)?2:3);
> -		if (res2) {
> +		VdbeBranchTaken(cmp_res != 0, (pOp->p5 & SQL_NULLEQ) ? 2 : 3);
> +		if (result)
>  			goto jump_to_p2;
> -		}
>  	}
>  	break;
>  }> diff --git a/src/box/tuple_compare.cc b/src/box/tuple_compare.cc
> index 3f8a0ce..6cbf555 100644
> --- a/src/box/tuple_compare.cc
> +++ b/src/box/tuple_compare.cc
> @@ -104,6 +104,12 @@ mp_classof(enum mp_type type)
>  	return mp_classes[type];
>  }
>  
> +int
> +mp_type_classes_comp(enum mp_type ltype, enum mp_type rtype)
> +{
> +	return mp_classof(ltype) - mp_classof(rtype);
> +}

13. Probably better keep it in one single usage place. Since it adds
unnecessary dependency on msgpuck.h for tuple_compare.h.

> +
>  static enum mp_class
>  mp_extension_class(const char *data)
>  {
> diff --git a/test/sql-tap/tkt3493.test.lua b/test/sql-tap/tkt3493.test.lua
> index 7ceec47..e5d7b2f 100755
> --- a/test/sql-tap/tkt3493.test.lua
> +++ b/test/sql-tap/tkt3493.test.lua
> @@ -45,7 +45,7 @@ test:do_execsql_test(
>      [[
>          SELECT 
>            CASE 
> -             WHEN B.val = 1 THEN 'XYZ' 
> +             WHEN B.val = '1' THEN 'XYZ' 

14. Leading whitespace.

>               ELSE A.val 
>            END AS Col1
>          FROM B  
15. I propose to add some tests specific to the tickets we fix.
Probably remove some existing tests which test the same. With more
edge cases. gcov should help to check that all conditions in this
patch are covered, especially about preparations of Mem to be used
in a comparison.

  reply	other threads:[~2020-03-29 22:29 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-20 12:34 imeevma
2020-03-29 22:29 ` Vladislav Shpilevoy [this message]
2020-03-30 11:38   ` Nikita Pettik
2020-03-30 16:53     ` Mergen Imeev
2020-04-02 23:47       ` Vladislav Shpilevoy
2020-04-21 12:42   ` Mergen Imeev
2020-04-26 18:22     ` Vladislav Shpilevoy
  -- strict thread matches above, loose matches on Subject: below --
2020-02-20  8:00 imeevma

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=876090db-cb61-0c46-427a-d5a856fad4f1@tarantool.org \
    --to=v.shpilevoy@tarantool.org \
    --cc=imeevma@tarantool.org \
    --cc=tarantool-patches@dev.tarantool.org \
    --cc=tsafin@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH v1 1/1] sql: remove implicit cast for COMPARISON' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

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