[Tarantool-patches] [PATCH v3 2/8] sql: change implicit cast for assignment

Nikita Pettik korablev at tarantool.org
Tue Jul 7 00:27:07 MSK 2020


On 05 Jul 17:26, Mergen Imeev wrote:
> Hi! Thank you for the review. My answers and new patch
> below. Sorry, I didn't include diff.
> > > commit 21d7145c1929bc4606c56e9a566477f248637ed1
> > > Author: Mergen Imeev <imeevma at gmail.com>
> > > Date:   Wed May 27 13:49:11 2020 +0300
> > > 
> > > + if (field_mp_plain_type_is_compatible(type, mp_type, true))
> > > +   return 0;
> > > + return -1;
> > > +}
> > > +
> > > +/**
> > > + * Convert the numeric value contained in MEM to double. If the
> > > + * is_precise flag is set, the conversion will succeed only if it
> > > + * is lossless.
> > > + *
> > > + * @param mem The MEM that contains the numeric value.
> > > + * @param is_precise Flag.
> > > + * @retval 0 if the conversion was successful, -1 otherwise.
> > > + */
> > > +static int
> > > +mem_convert_to_double(struct Mem *mem, bool is_precise)
> > > +{
> > > + if ((mem->flags & (MEM_Int | MEM_UInt)) == 0)
> > > +   return -1;
> > > + if ((mem->flags & MEM_Int) != 0) {
> > > +   int64_t i = mem->u.i;
> > > +   double d = (double)i;
> > > +   if (!is_precise || i == (int64_t)d)
> > > +     mem_set_double(mem, d);
> > > +   else
> > > +     return -1;
> > > + } else {
> > > +   uint64_t u = mem->u.u;
> > > +   double d = (double)u;
> > > +   if (!is_precise || u == (uint64_t)d)
> > > +     mem_set_double(mem, d);
> > > +   else
> > > +     return -1;
> > > + }
> > > + mem->field_type = FIELD_TYPE_DOUBLE;
> > 
> > Why not incorparate field_type assingment into mem_set_double()?
> > The same concerns other converting functions.
> > 
> I think this should be done in #4906 issue since it may
> lead to not always simple to understand changes.

Эм, не очень понял как трактовать твой ответ. Типо тут работает,
а если там засунуть, то что-то начинает падать..? 4906 немного про
другое: в любом случае у нас в меме будет и field_type и mp_type.
К тому же, и то, и это - рефакторинг и функциональных изменений быть
не должно.
 
> > > +/**
> > > + * Convert the numeric value contained in MEM to another numeric
> > > + * type. If the is_precise flag is set, the conversion will
> > > + * succeed only if it is lossless.
> > > + * @param mem The MEM that contains the numeric value.
> > > + * @param type The type to convert to.
> > > + * @param is_precise Flag.
> > > + * @retval 0 if the conversion was successful, -1 otherwise.
> > > + */
> > > +static int
> > > +mem_convert_numeric(struct Mem *mem, enum field_type type, bool is_precise)
> > 
> > mem_convert_to_numeric ?
> > mem_cast_to_numeric
> > 
> Used first option.
> 
> > > +{
> > > + if (!sql_type_is_numeric(type) ||
> > > +     (mem->flags & (MEM_Real | MEM_Int | MEM_UInt)) == 0)
> > 
> > It's somehow unnatural passing to _numeric function non-numeric type
> > to convert to. Instead let's use this function properly and replace
> > branching with assertion.
> > 
> No, I think it is good as it is. More that that, due to
> some other comments I added similar checks to all other
> mem_convert_to>* functions.

В остальных случаях ты туда тип вообще не передаешь.
 
> My reason is: in all places we use this function we have
> to check that field type and MEM_type are numeric, so it
> make sence to move this check inside of the function.

Еще раз: это полный идиотизм передавать ТИП (отличный от нумерика) к
которому ты собираешься кастовать в функцию, которая по названию
предполагает каст к нумерику. Считай, что я настаиваю на этом
изменении.

> > > +   return -1;
> > > + if (type == FIELD_TYPE_NUMBER)
> > > +   return 0;
> > > + if (type == FIELD_TYPE_DOUBLE)
> > > +   return mem_convert_to_double(mem, is_precise);
> > > + if (type == FIELD_TYPE_UNSIGNED)
> > > +   return mem_convert_to_unsigned(mem, is_precise);
> > > + assert(type == FIELD_TYPE_INTEGER);
> > > + return mem_convert_to_integer(mem, is_precise);
> > > +}
> > > +
> > >  /*
> index 37283e506..bd0055668 100644
> --- a/src/box/sql/sqlInt.h
> +++ b/src/box/sql/sqlInt.h
> @@ -397,6 +397,15 @@ sql_value_to_diag_str(sql_value *value);
>  enum mp_type
>  sql_value_type(sql_value *);
>  
> +/*
> + * Return the MP_type of the value of the MEM.
> + *
> + * @param mem MEM with the correct MEM_type.
> + * @retval MP_type of the value.
> + */
> +enum mp_type
> +mem_mp_type(struct Mem *mem);

Я вытащил добавление этой функции в отдельный патч и добавил
рефакторинг чтобы соеденить ее с sql_value_type().
https://github.com/tarantool/tarantool/tree/np/gh-3809-remastered

Если ты ОК, я пушну вне очереди в мастер.

> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> index 950f72ddd..1386ea2c0 100644
> --- a/src/box/sql/vdbe.c
> +++ b/src/box/sql/vdbe.c
> +		uint64_t u = mem->u.u;
> +		double d = (double)u;
> +		if (!is_precise || u == (uint64_t)d)
> +			mem_set_double(mem, d);
> +		else
> +			return -1;
> +	}
> +	mem->field_type = FIELD_TYPE_DOUBLE;
> +	return 0;
> +}
> +
> + * P1 are compatible with given field types in P4.
> + * If the MEM_type of the value and the given type are
> + * incompatible according to field_mp_plain_type_is_compatible(),
> + * but both are numeric, this opcode attempts to convert the value
> + * to the type.
> + */
> +case OP_ImplicitCast: {
> +	enum field_type *types = pOp->p4.types;
> +	assert(types != NULL);
> +	assert(types[pOp->p2] == field_type_MAX);
> +	for (int i = 0; types[i] != field_type_MAX; ++i) {
> +		enum field_type type = types[i];
> +		pIn1 = &aMem[pOp->p1 + i];
> +		assert(pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)]);
> +		assert(memIsValid(pIn1));
> +		if (mem_is_type_compatible(pIn1, type))
> +			continue;
> +		if (mem_convert_to_numeric(pIn1, type, false) == 0)

Инвертируй, пожайлуйста условие:

if (mem_convert_to_numeric(pIn1, type, false) !=) {
	//raise an error
}

Это будет больще соответствовать нашему кодстайлу.

> +			continue;
> +		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
> +			 sql_value_to_diag_str(pIn1), field_type_strs[type]);
> +		goto abort_due_to_error;
> +	}
> +	break;
> +}
> +
>  /* Opcode: MakeRecord P1 P2 P3 P4 P5
>   * Synopsis: r[P3]=mkrec(r[P1 at P2])
>   *
> diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
> index 4e103a653..b106ce6bd 100644
> --- a/src/box/sql/vdbeapi.c
> +++ b/src/box/sql/vdbeapi.c
> @@ -225,6 +225,33 @@ sql_value_type(sql_value *pVal)
>  	}
>  }
>  
> +enum mp_type
> +mem_mp_type(struct Mem *mem)

Еще раз: это не апишная функция (хотя бы в терминологии скулайта),
ей место скорее в vdbemem.c. См. мой рефакторинг.

> +{
> --- a/test/sql-tap/autoinc.test.lua
> +++ b/test/sql-tap/autoinc.test.lua

Тесты не смотрел, предполагая что нет изменений по сравнению с
предыдущей версией.



More information about the Tarantool-patches mailing list