[Tarantool-patches] [PATCH v1 1/1] sql: enable autoindex optimization

Мерген Имеев imeevma at tarantool.org
Tue Sep 22 08:25:15 MSK 2020


Прошу прощения, я случайно отправил не полностью написанное письмо.

  
>Вторник, 22 сентября 2020, 8:03 +03:00 от Мерген Имеев <imeevma at tarantool.org>:
> 
>Привет! Спасибо за ревью! Я прошу прощения, на данный момент я могу только ответить на вопросы ревьюё Все изменения и примеры я смогу отправить завтра.
>  
>>Вторник, 22 сентября 2020, 0:37 +03:00 от Vladislav Shpilevoy < v.shpilevoy at tarantool.org >:
>> 
>>Привет! Спасибо за патч!
>>
>>On 20.09.2020 23:17, Mergen Imeev wrote:
>>> Hi! Thank you for the review. My answers, diff and new patch below.
>>>
>>> I tried to answer a few qustions, but at the end decided to not include them
>>> in this patch. Here are these questions:
>>
>>Давай по-русски. Я че-то не понимаю ничего. Я этих вопросов не задавал. Это
>>твои собственные вопросы для самопроверки, на которые ты решил ответить
>>публично?
>
>Мне казалось, что эти вопросы имеют значение для этого патча, поэтому я включил их сюда.
>>
>>> 1) Can we use secondary index instead of an ephemeral space?
>>> This look a bit difficult, and it cannot be used with view since they do not
>>> have an actual index.
>>
>>Это не только сложно, но и неправильно. Создавать вторичный индекс на каждый
>>запрос выглядит очень жестко. Кто будет гарантировать их удаление (так как
>>это изменение схемы, а значит попадает в WAL? Как сделать их невидимыми для
>>юзеров, и "видеть" их только в одном запросе? Как быть с винилом? Слишком
>>много вопросов.
>
>Понял, спасибо. У меня были вопросы другого плана — как работать с этим вторичным индексом что-бы все не сломалось в SQL, но твои вопросы более глобальны. Мне казалось, что создание индекса вместо пересоздания таплов будет выгоднее, но возникает слишком много проблем. Я думаю пока стоит отказаться от этого варианта.
> 
>>
>>Хотя в далекой перспективе звучит круто - "эфемерные индексы". Прямо таки
>>название для дисера. Могло бы помочь в мемтиксе, чтоб "эфемерные индексы"
>>хранили ссылки на таплы, а не копии таплов.
>>
>>> 2) Can we get rid of 'viaCoroutine' branches in constructAutomaticIndex()?
>>> As far as I understand, this flag is always false here.
>>
>>Не представляю что это. И с патчем походу не связано.
>
>Не совсем не связано. Дело в том, что если мы удалим бранч с viaCoroutine, т.к. он у нас на данный момент всегда false, у нас упростится код. Например функция generate_index_key() станет void.
> 
>>
>>> 3) Can I get rid of OP_Realify?
>>> I think I can, but decided to do this no in this patch.
>>
>>Ты пробовал его просто дропнуть? Ломается что-то?
>
>Пробовал, ничего не ломается. Если посмотреть на все места использования OP_Realify (их два) и посмотреть на старый код, еще до удалаления типа Real из SQL, то там видно, что этот опкод использовался для преобразования INT в REAL. Когда REAL быз заменен на NUMBER это преобразование осталось, хотя, как мне кажется, в нем небыло нужды. После добавления DOUBLE в паре мест этот опкод все еще существует, однако используется неправильно. Я думаю этот опкод больше ненужен. Однако, мне кажется его стоит убрать отдельным патчем вне этой задачи.
>>
>>> On Wed, Sep 09, 2020 at 11:58:09PM +0200, Vladislav Shpilevoy wrote:
>>>> Hi! Thanks for the patch!
>>>>
>>>> See 14 comments below.
>>>>
>>>> On 04.09.2020 13:53,  imeevma at tarantool.org wrote:
>>>>> This patch enables the "auto-index" optimization in SQL. The auto-index
>>>>> is actually an ephemeral space that contain some columns of the original
>>>>> space.
>>>>
>>>> 1. In the patch the code calls the index 'covering' meaning that it
>>>> contains all the columns. What is true?
>>> Both, I think, since this index is the PK of mentioned ephemeral space.
>>
>>Что значит both? Индекс либо covering (содержит все колонки), либо нет (содержит
>>не все).
>
>Суть в том, что idx_def содержит не все колонки оригинального спейса, однако индекс создаваемый для эфемерного спейса содержит все колонки. При этом, создание idx_def не приводит к созданию индекса.
> 
>Я попробую описать весь механизм:
>*  Планировщик определяет, что будет использоваться автоматический индекс.
>*  Создается idx_def, который содержит все использующиеся в запросе колонки оригинального спейса. Не только те, которые используются во where. Это делается для того, что бы больше не обращаться к оригинальному спейсу, а работать только с эфемерным спейсом. Этот idx_def не используется для создания индекса.
>*  Создается эфемерный спейс на основе созданного ранее idx_def. Помимо колонок оригинального спейса добавляется rowid, т.к. возможны случаи, когда значения во всех колонках совпадает в нескольких записях. При этом, колонки в эфемерном спейсе расположены в том же порядке, в каком они описаны в индексе.  Т.е. они, скорее всего, расположены не в том же порядке, в каком они расположены в оригинальном спейсе.
>*  Для созданного эфемерного спейса создается индекс, которые является covering. Именно поэтому в некоторых местах написано, что создается covering index.
>*  Т.к. планировщик посчитал, что будет использоваться автоиндекс, то в качестве спейса из которого будут выбраны таплы мы будем использовать созданный нами эфемерный спейс. Однако, во время построения vdbe-кода в качестве fieldno было использовано расположение колонок в оригинальном спейсе. Поэтому, в случае использования автоиндекса мы заменяем fieldno оригинального спейса в OP_Column на fieldno в эфемерном спейсе используя созданный ранее idx_def.
> 
>>
>>> We create index definition for the original space, however this index definition
>>> is not used for any actual index. This index definition is used as a connection
>>> between original space and created ephemeral space.
>>
>>Не понял ничего. Перефразируй на русском плиз, может так понятнее станет.
>
>Здесь я имел ввиду то, что описал в п.2 и  п.5 выше.
>>
>>>>> In some cases, this can speed up execution, since creating a new
>>>>> index and searching using it can be more cost efficient than scanning.
>>>>
>>>> 2. Could you provide an example?
>>> At the moment examples that can show that in some cases SQL works faster with
>>> enabled optimization are quite big. One of such examples is Q13 for TPC-H.
>>> Should I provide data generation and query?
>>
>>Нет, скрипт слишком большой наверняка и не скажет ничего. Я пытаюсь понять,
>>для каких запросов это применимо. Как эти запросы описать? Как по запросу понять,
>>будет там автоиндекс или нет? Конкретный пример запроса может и поможет, но я
>>хз, я просто не знаю как он выглядит.
>
>Понял. Я добавлю пример и описание того, котгда автоиндекс применяется.  На данный момент могу сказать, что одним из случаев когда он применяется — запросы с использованием join. Этот вопрос я опишу более попдробно чуть позже.
>>
>>Вот ты добавил "Auto-index" optimization is now enabled в changelog. Я юзер, и не
>>представляю что это такое. Ты отправишь меня читать код TPC-H бенча, чтобы понять?
>
>Понял,  исправлю.
>>
>>>>> Co-authored-by: Mergen Imeev < imeevma at gmail.com >
>>>>> Co-authored-by: Timur Safin < tsafin at tarantool.org >
>>>>> ---
>>>>>  https://github.com/tarantool/tarantool/issues/4933
>>>>>  https://github.com/tarantool/tarantool/tree/imeevma/gh-4933-autoindex
>>>>>
>>>>> @ChangeLog
>>>>> - "Auto-index" optimization is now enabled (gh-4933).
>>>>>
>>>>> src/box/CMakeLists.txt | 2 +-
>>>>> src/box/sql.c | 2 +-
>>>>> src/box/sql/delete.c | 16 ++--
>>>>> src/box/sql/sqlInt.h | 8 +-
>>>>> src/box/sql/where.c | 170 +++++++++++++++++++++++------------
>>>>> src/box/sql/wherecode.c | 13 +--
>>>>> test/sql-tap/whereF.test.lua | 16 +++-
>>>>> 7 files changed, 151 insertions(+), 76 deletions(-)
>>>>>
>>>>> diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
>>>>> index b8b2689d2..7e3ad0e22 100644
>>>>> --- a/src/box/CMakeLists.txt
>>>>> +++ b/src/box/CMakeLists.txt
>>>>> @@ -217,7 +217,7 @@ add_library(box STATIC
>>>>> if(CMAKE_BUILD_TYPE STREQUAL "Debug")
>>>>> add_definitions(-DSQL_DEBUG=1)
>>>>> endif()
>>>>> -add_definitions(-DSQL_OMIT_AUTOMATIC_INDEX=1 -DSQL_TEST=1)
>>>>> +add_definitions(-DSQL_TEST=1)
>>>>
>>>> 3. I still see SQL_OMIT_AUTOMATIC_INDEX in src/box/sql/where.c.
>>> I think the original idea was to make an option to disable this optimization.
>>> Since such thing is already exists, I decided to not remove it.
>>
>>У нас нет конфигурации опций сборки SQL. Мы их все выпиливаем, так как билды
>>у нас под один конкретный набор опций. Есть runtime опции, но это ничего общего
>>с макросами не имеет. Если этот макрос более не указывается, то надо его
>>удалить отовсюду.
>
>Понял, исправлю. Я думаю в этом случае я добавлю новую опцию в session_settings, которая будет отключать эту оптимизацию.
>>
>>>>> set(EXT_SRC_DIR ${CMAKE_SOURCE_DIR}/extra)
>>>>> set(EXT_BIN_DIR ${CMAKE_BINARY_DIR}/extra)
>>>>> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
>>>>> index 68abd1f58..57478c129 100644
>>>>> --- a/src/box/sql/delete.c
>>>>> +++ b/src/box/sql/delete.c
>>>>> @@ -546,24 +546,25 @@ sql_generate_row_delete(struct Parse *parse, struct space *space,
>>>>> }
>>>>>
>>>>> int
>>>>> -sql_generate_index_key(struct Parse *parse, struct index *index, int cursor,
>>>>> - int reg_out, struct index *prev, int reg_prev)
>>>>> +sql_generate_index_key(struct Parse *parse, struct index_def *idx_def,
>>>>> + int cursor, int reg_out, struct index *prev,
>>>>> + int reg_prev, int reg_eph)
>>>>
>>>> 4. The function has nothing to do with ephemeral spaces. It just does not
>>>> care whether its space is ephemeral. Its task is to just assemble a key,
>>>> not caring about space type. Why did you make it always work with an
>>>> ephemeral space? Won't this will affect normal spaces - they don't need
>>>> OP_NextIdEphemeral or whatever else is related to ephemerals.
>>>>
>>>> In the end of the review I realized the function is never used for anything
>>>> except automatic index. Moreover, prev and reg_prev are NULL and 0 always.
>>>> I suggest to move this function into the file, which needs it; make it
>>>> 'static'; remove 'prev' and 'reg_prev' args; rename it to something closer to
>>>> what it actually does.
>>> Done. I refactored this function a bit while moving. However, I decided to
>>> not remove part with 'OP_Realify', even though I think this is deprecared code.
>>> From what I see, this opcode is outdated and should be removed, but not in this
>>> patch. I will send a new patch later, as a refactoring.
>>>
>>>>
>>>> 6. The function talks about 'covering' index, but covering makes no
>>>> sense in Tarantool. It is not possible to fetch a part of tuple. All
>>>> indexes in Tarantool, from box API point of view, are covering. So
>>>> why is this concept still here? Can we remove it and simplify things?
>>>>
>>> It is true that we can get only a whole tuple, however the main feature of the
>>> covering indexes that it contains all needed information about space field.
>>
>>Нет, в терминологии sqlite covering означает именно наличие всех колонок. И в
>>этом смысле оно в коде и осталось. Это было нужно, так как индексы в sqlite
>>хранят только ключевые колонки. Если запрос имел колонки не из индекса, нужно
>>было делать поиск в таблице (!= индекс). После covering индексов делать второй
>>поиск было не нужно.
>
>Используемый индекс содержит все колонки эфемерного спейса, поэтому он covering.
> 
>>
>>Такой смысл вкладывается covering, и в таком смысле он ничего в тарантуле не
>>значит, так как не-covering есть только в виниле, но
>>1) публичного апи для доступа к сырым индексам винила нет;
>>2) все равно нужен поиск в первичном индексе, так как вторичный индекс может
>>содержать удаленный мусор в случае наших LSM-деревьев.
>
>Ок, понял. Изучу э

Ок, понял. Изучу вопрос с covering в SQL подробнее.
>>
>>> For example, we do not need to look at the space definition to find field type if
>>> we have covering index.
>>
>>Да, но это опять же не связано никак с понятием covering.
>>
>>> It may be not so important in Tarantool as it was in
>>> SQLite, however this concept is used quite often in SQL code.
>>
>>Это легаси от sqlite. В тарантуле все индексы считаются covering.
>>
>>> I do not think
>>> that we should fix this issue here.
>>
>>Это зависит от того, насколько это усложняет код, чтобы раздрачивать эти
>>covering/не-covering. Судя по коду я так понимаю, что в эфемерный спейс
>>попадают не все колонки оригинального спейса, а только нужные для индекса?
>>Или для индекса нужны как раз все?
>
>В эфемерный спейс попадают все используемые в запросе колонки, не только те, что используются во where. При этом это скорее всего не все колонки оригинального спейса.
>>
>>> Also, even though we get a whole tuple, we actually use only needed columns
>>> from the tuple to create a new tuple and push it to the new ephemeral space.
>>
>>Если для тапла в эфемерном спейсе хранятся не все колонки оригинального спейса,
>>то это не covering с точки зрения оригинального спейса.

Мне кажется с точки зрения индекса это covering. Однако, скоре всего ты прав - с точки зрения индекса это covering, а с точки зрения «auto-index» в целом это будет не covering. Исправлю этот момент.
>>
>>>>> + sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph, nKeyCol + 1, 0,
>>>>> + (char *)pk_info, P4_KEYINFO);
>>>>> + sqlVdbeAddOp3(v, OP_IteratorOpen, pLevel->iIdxCur, 0, reg_eph);
>>>>> + VdbeComment((v, "for %s", space->def->name));
>>>>>
>>>>> /* Fill the automatic index with content */
>>>>> sqlExprCachePush(pParse);
>>>>> @@ -2841,12 +2885,12 @@ tnt_error:
>>>>> * those objects, since there is no opportunity to add schema
>>>>> * indexes on subqueries and views.
>>>>> */
>>>>> - pNew->rSetup = rLogSize + rSize + 4;
>>>>> - if (!pTab->def->opts.is_view &&
>>>>> - pTab->def->id == 0)
>>>>> - pNew->rSetup += 24;
>>>>> - if (pNew->rSetup < 0)
>>>>> - pNew->rSetup = 0;
>>>>> + pNew->rSetup = rLogSize + rSize;
>>>>> + if (!space->def->opts.is_view &&
>>>>> + space->def->id == 0)
>>>>> + pNew->rSetup += 28;
>>>>> + else
>>>>> + pNew->rSetup -= 10;
>>>>
>>>> 10. What is this? What are the numbers?
>>> These numbers I got from SQLite. Actually, we should find these numbers
>>> experementally, but I still do not have a proper way to do this.
>>>
>>> There is one more place where we should experemetally find a function
>>> of number of tuples in the space which allows us to decide more carefully when
>>> we want to use the optimization. Here it is:
>>> "rSize = DEFAULT_TUPLE_LOG_COUNT;"
>>>
>>> As you can see, this function is a constant for now. This is also from SQLite.
>>>
>>> In general, we should use statistics here, but we do not have it at the moment.
>>>
>>> These numbers are used to decide when to use auto-index. I think that since at
>>> the moment all these values are constants, auto-index is used always. This is
>>> obviously wrong. I plan to fix it as soon as I find a proper way to test
>>> performance.
>>
>>Здесь нужен как минимум комментарий в коде, что это за числа. Тот комментарий, что
>>есть, очевидно устарел. Он говорит, что стоимость для view - 4, а у тебя - -10. То
>>есть для view ты считаешь, что лучше создать авто-индекс, чем не создавать, всегда.
>>Нет? Если так, то отрицательная стоимость выглядит не очень правильно.
Изучу этот вопрос подробнее и отпишусь в следующем письме.
>>
>>>>> /* TUNING: Each index lookup yields 20 rows in the table. This
>>>>> * is more than the usual guess of 10 rows, since we have no way
>>>>> * of knowing how selective the index will ultimately be. It would
>>>>> @@ -4794,18 +4838,34 @@ sqlWhereEnd(WhereInfo * pWInfo)
>>>>> + continue;
>>>>> + }
>>>>> + /*
>>>>> + * In case we are using auto-indexing, we have
>>>>> + * to change the column number, because in
>>>>> + * ephemeral space columns are in the same order
>>>>> + * as they described in key_def. So, instead of
>>>>> + * the field number, we have to insert index of
>>>>> + * the part with this fieldno.
>>>>
>>>> 12. Why are the columns reordered?
>>> See above. Should I add this explanation somewhere is the patch?
>>
>>Если в авто-индексе только ключевые колонки, от откуда в result set попадают
>>неключевые колонки, которые в WHERE не участвовали? Делается обратно лукап
>>в оригинальном спейсе что ли?
В эферный спейс попадают все колонки, использующиеся в запросе. Поэтому нам больше нет нужды смотреть в оригинальный спейс.
>>
>>>>> + */
>>>>> + struct key_def *key_def =
>>>>> + pLevel->pWLoop->index_def->key_def;
>>>>> + uint32_t part_count = key_def->part_count;
>>>>> + for (uint32_t i = 0; i < part_count; ++i) {
>>>>> + if ((int)key_def->parts[i].fieldno == x)
>>>>> + pOp->p2 = i;
>>>>> }
>>>>> }
>>>>> }
>>>>> diff --git a/test/sql-tap/whereF.test.lua b/test/sql-tap/whereF.test.lua
>>>>> index 5a894b748..3235df437 100755
>>>>> --- a/test/sql-tap/whereF.test.lua
>>>>> +++ b/test/sql-tap/whereF.test.lua
>>>>> @@ -90,10 +90,20 @@ test:do_execsql_test(
>>>>>
>>>>> -- for _ in X(0, "X!foreach", [=[["tn sql","\n 1 \"SELECT * FROM t1, t2 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e\"\n 2 \"SELECT * FROM t2, t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e\"\n 3 \"SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e\"\n"]]=]) do
>>>>> for tn, sql in ipairs({"SELECT * FROM t1, t2 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e",
>>>>> - "SELECT * FROM t2, t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e",
>>>>> - "SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e"}) do
>>>>> + "SELECT * FROM t2, t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e"}) do
>>>>> test:do_test(
>>>>> - "2."..tn,
>>>>> + "2.1."..tn,
>>>>> + function()
>>>>> + return test:execsql("EXPLAIN QUERY PLAN "..sql)
>>>>> + end, {
>>>>> + '/SEARCH TABLE T1/',
>>>>> + '/SEARCH TABLE T2/'
>>>>> + })
>>>>> +end
>>>>> +
>>>>> +for tn, sql in ipairs({"SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e"}) do
>>>>> + test:do_test(
>>>>> + "2.2."..tn,
>>>>> function()
>>>>> return test:execsql("EXPLAIN QUERY PLAN "..sql)
>>>>> end, {
>>>>
>>>> 14. Shouldn't there be a test showing 'AUTOMATIC' keyword in the execution plan?
>>> Actually it does, in the test above.
>>
>>Во всем whereF.test.lua файле слово automatic грепается только в каком-то комменте.
Понял, исправлю и отпишу в следующем письме.
>>
>>> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
>>> index c5f79f908..f6b533ab1 100644
>>> --- a/src/box/sql/where.c
>>> +++ b/src/box/sql/where.c
>>> @@ -712,10 +712,61 @@ termCanDriveIndex(WhereTerm * pTerm, /* WHERE clause term to check */
>>> #endif
>>>
>>> #ifndef SQL_OMIT_AUTOMATIC_INDEX
>>> +
>>> +/**
>>> + * Generate code that will assemble an index key, adds rowid and stores it in
>>> + * register reg_out. The key will be for index which is an
>>> + * index on table. cursor is the index of a cursor open on the
>>> + * table table and pointing to the entry that needs indexing.
>>> + * cursor must be the cursor of the PRIMARY KEY index.
>>
>>Лимит на ширину комента расширен до 80.
>>
>>Кроме того, ты читал этот коммент? Что за 'index which is an index on table'?
>>Какой 'table'? Что за 'cursor open on the table table'? Я как будто
>>контрольные фразы из Бегущего По Лезвию читаю.
Понял, исправлю и отпишусь в следующем письме.
>>
>>Параметры у тебя описаны в двух местах - вверху и в @param. Должно быть
>>одно место.
>>
>>> + *
>>> + * @param parse Parsing context.
>>> + * @param idx_def The index definition for which to generate a key.
>>> + * @param cursor Cursor number from which to take column data.
>>> + * @param reg_out Put the new key into this register if not NULL.
>>> + * @param reg_eph Register holding adress of ephemeral space.
>>> + *
>>> + * @retval Return a register number which is the first in a block
>>
>>@retval принимает два аргумента - значение и описание. То есть ты описал, что
>>функция возвращает значение 'Return' с описанием 'a register number ...'. Для
>>описания без конкретного значения есть @return/@returns.
Понял, исправлю.
>>
>>> + * of registers that holds the elements of the index key. The
>>> + * block of registers has already been deallocated by the time
>>> + * this routine returns.
>>> + */
>>> +static int
>>> +generate_index_key(struct Parse *parse, struct index_def *idx_def,
>>
>>Было бы неплохо в названии функции отразить, что она работает только с эфемерными
>>спейсами.

Понял, исправлю.
>>
>>> + int cursor, int reg_out, int reg_eph)
>>> +{
>>> + assert(reg_out != 0);
>>> + struct Vdbe *v = parse->pVdbe;
>>> + int col_cnt = idx_def->key_def->part_count;
>>> + int reg_base = sqlGetTempRange(parse, col_cnt + 1);
>>> + for (int j = 0; j < col_cnt; j++) {
>>> + uint32_t tabl_col = idx_def->key_def->parts[j].fieldno;
>>> + sqlVdbeAddOp3(v, OP_Column, cursor, tabl_col, reg_base + j);
>>> + /*
>>> + * If the column type is NUMBER but the number
>>> + * is an integer, then it might be stored in the
>>> + * table as an integer (using a compact
>>> + * representation) then converted to REAL by an
>>> + * OP_Realify opcode. But we are getting
>>> + * ready to store this value back into an index,
>>> + * where it should be converted by to INTEGER
>>> + * again. So omit the OP_Realify opcode if
>>> + * it is present
>>> + */
>>> + sqlVdbeDeletePriorOpcode(v, OP_Realify);
>>> + }
>>> + sqlVdbeAddOp2(v, OP_NextIdEphemeral, reg_eph, reg_base + col_cnt);
>>> + sqlVdbeAddOp3(v, OP_MakeRecord, reg_base, col_cnt + 1, reg_out);
>>> +
>>> + sqlReleaseTempRange(parse, reg_base, col_cnt);
>>> + return reg_base;
>>> +}
>>> +
>>> /*
>>> - * Generate code to construct the Index object for an automatic index
>>> - * and to set up the WhereLevel object pLevel so that the code generator
>>> - * makes use of the automatic index.
>>> + * Generate code to construct the ephemeral space that contains used in query
>>> + * fields of one of the tables. The index of this ephemeral space will be known
>>> + * as an "automatic index". Also, this functions set up the WhereLevel object
>>> + * pLevel so that the code generator makes use of the automatic index.
>>> */
>>> static void
>>> constructAutomaticIndex(Parse * pParse, /* The parsing context */
>>> @@ -801,9 +852,11 @@ constructAutomaticIndex(Parse * pParse, /* The parsing context */
>>> n = 0;
>>> idxCols = 0;
>>> uint32_t size = sizeof(struct key_part_def) * nKeyCol;
>>> - struct key_part_def *part_def = malloc(size);
>>> + struct region *region = &fiber()->gc;
>>> + size_t used = region_used(region);
>>> + struct key_part_def *part_def = region_alloc(region, size);
>>
>>Это раздолбает с ASANом из-за отсутствия выравнивания. Нужен region_alloc_array.
Исправлю, но мне казалочь, что это временый объект и его выравнивание нигде не проверяется.
>>
>>Почему не используешь Parse.region? Файберный обязательно где-нибудь проебется
>>почистить и начнет течь.
Понял, исправлю.
>>
>>> if (part_def == NULL) {
>>> - diag_set(OutOfMemory, size, "malloc", "part_def");
>>> + diag_set(OutOfMemory, size, "region_alloc", "part_def");
>>> pParse->is_aborted = true;
>>> return;
>>> }
>>> @@ -867,7 +919,7 @@ constructAutomaticIndex(Parse * pParse, /* The parsing context */
>>> assert(n == nKeyCol);
>>>
>>> struct key_def *key_def = key_def_new(part_def, nKeyCol, false);
>>> - free(part_def);
>>> + region_truncate(region, used);
>>
>>Смысла в этом мало. Либо регион и забить на освобожения, либо куча.

Понял, уберу после замены региона с файберного на парсерный.
>>
>>> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
>>> index e9e936856..f6b533ab1 100644
>>> --- a/src/box/sql/where.c
>>> +++ b/src/box/sql/where.c
>>> @@ -712,10 +712,61 @@ termCanDriveIndex(WhereTerm * pTerm, /* WHERE clause term to check */
>>> #endif
>>>
>>> #ifndef SQL_OMIT_AUTOMATIC_INDEX
>>> +
>>> +/**
>>> + * Generate code that will assemble an index key, adds rowid and stores it in
>>> + * register reg_out. The key will be for index which is an
>>> + * index on table. cursor is the index of a cursor open on the
>>> + * table table and pointing to the entry that needs indexing.
>>> + * cursor must be the cursor of the PRIMARY KEY index.
>>> + *
>>> + * @param parse Parsing context.
>>> + * @param idx_def The index definition for which to generate a key.
>>
>>Походу тебе достаточно передавать key_def. Весь idx_def не нужен.
Понял, исправлю.
>>
>>> + * @param cursor Cursor number from which to take column data.
>>> + * @param reg_out Put the new key into this register if not NULL.
>>> + * @param reg_eph Register holding adress of ephemeral space.
>>> + *
>>> + * @retval Return a register number which is the first in a block
>>> + * of registers that holds the elements of the index key. The
>>> + * block of registers has already been deallocated by the time
>>> + * this routine returns.
>>> + */
>>> +static int
>>> +generate_index_key(struct Parse *parse, struct index_def *idx_def,
>>> + int cursor, int reg_out, int reg_eph)
>>> +{
>>> + assert(reg_out != 0);
>>> + struct Vdbe *v = parse->pVdbe;
>>> + int col_cnt = idx_def->key_def->part_count;
>>> + int reg_base = sqlGetTempRange(parse, col_cnt + 1);
>>> + for (int j = 0; j < col_cnt; j++) {
>>> + uint32_t tabl_col = idx_def->key_def->parts[j].fieldno;
>>> + sqlVdbeAddOp3(v, OP_Column, cursor, tabl_col, reg_base + j);
>>> + /*
>>> + * If the column type is NUMBER but the number
>>> + * is an integer, then it might be stored in the
>>> + * table as an integer (using a compact
>>> + * representation) then converted to REAL by an
>>> + * OP_Realify opcode. But we are getting
>>> + * ready to store this value back into an index,
>>> + * where it should be converted by to INTEGER
>>> + * again. So omit the OP_Realify opcode if
>>> + * it is present
>>> + */
>>> + sqlVdbeDeletePriorOpcode(v, OP_Realify);
>>> + }
>>> + sqlVdbeAddOp2(v, OP_NextIdEphemeral, reg_eph, reg_base + col_cnt);
>>> + sqlVdbeAddOp3(v, OP_MakeRecord, reg_base, col_cnt + 1, reg_out);
>>> +
>>> + sqlReleaseTempRange(parse, reg_base, col_cnt);
>>
>>Выглядит как лютый хак - аллоцируется N + 1 временных регистров, но
>>освобождается только N, чтоб в последем че-то сохранить. То есть намеренная
>>утечка временного регистра на время выполнения запроса. Выглядит не очень.
>>Лучше бы это починить отдельным тикетом.

Это не хак, а скорее баг. Я исправлю количество освождаемых mem на N + 1. А то, что используется N + 1 мем — это нужно исправить на N + 2. При этом, этот мем используется в только в бранче оператора if, который сейчас всегда пропускается т.к. viaCoroutine всегда false в этой функции. Я пока не смог проверить работоспособность этого бранча.
> 
>-- <br/>Мерген Имеев
>  
 
 
-- <br/>Мерген Имеев
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.tarantool.org/pipermail/tarantool-patches/attachments/20200922/88bf9f79/attachment.html>


More information about the Tarantool-patches mailing list