[tarantool-patches] Re: [PATCH v3 4/4] sql: Region-based allocations.
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Thu Apr 26 14:47:23 MSK 2018
Hello. Thanks for contributing! See below 16 comments.
On 25/04/2018 19:52, Kirill Shcherbatov wrote:
> Function sql_field_retrieve could leak names
> in error cases. Let allocate all memory with
> region allocator.
>
> Needed for #3272.
1. It is not only 3272. It is a part of the separate region
allocations issue. I do not remember the number, please, find and
write here 'Part of #nnnn#' on new line.
> diff --git a/src/box/sql.c b/src/box/sql.c
> index 6d4255e..2893d70 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1685,22 +1685,43 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno)
> return space->def->fields[fieldno].default_value_expr;
> }
>
> +struct space_def *
> +sql_ephemeral_space_def_new(Parse *parser)
> +{
> + struct space_def *def = NULL;
> + struct region *region = &fiber()->gc;
> + size_t size = sizeof(struct space_def) + 1;
> + def = (struct space_def *)region_alloc(region, size);
> + if (def != NULL) {
> + memset(def, 0, size);
> + def->dict =
> + (struct tuple_dictionary *)
> + region_alloc(region,
> + sizeof(struct tuple_dictionary));
2. Why do you need a dictionary? It must not be used in SQL anywhere. And
please, do not mess checks on allocations error - try to check it right after
allocation. It is hard to review code, where after alloc its result is checked
multiple times on ==/!= NULL. If you have multiple allocations, and after fail of
each one you must destroy some results of the previous ones, or set some error
codes, you can use this way:
if (alloc1_failed)
goto error1;
...
if (alloc2_failed)
goto error2;
...
error3:
do_clean3...
error2:
do_clean2...
error1:
do_clean1...
return -1;
See the good example in vy_lsm_new() function.
> + }
> + if (def == NULL || def->dict == NULL) {
> + parser->rc = SQLITE_NOMEM_BKPT;
> + parser->nErr++;
> + return NULL;
> + }
> + def->dict->refs = 1;
> + def->opts.temporary = true;
> + return def;
> +}
> +
> Table *
> sql_ephemeral_table_new(Parse *parser)
> {
> sqlite3 *db = parser->db;
> struct space_def *def = NULL;
> Table *table = sqlite3DbMallocZero(db, sizeof(Table));
> - if (table != NULL) {
> - def = space_def_new(0, 0, 0, NULL, 0, NULL, 0,
> - &space_opts_default, NULL, 0);
> - }
> + if (table != NULL)
> + def = sql_ephemeral_space_def_new(parser);
> if (def == NULL) {
> sqlite3DbFree(db, table);
> - parser->rc = SQLITE_NOMEM_BKPT;
> - parser->nErr++;
3. Why did you remove it? Region allocation error is the same, as
malloc allocation error. See details in 6.
> return NULL;
> }
> +
4. Garbage diff.
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 584e6b1..4db4356 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -386,7 +386,9 @@ deleteTable(sqlite3 * db, Table * pTable)
> sqlite3DbFree(db, pTable->zColAff);
> sqlite3SelectDelete(db, pTable->pSelect);
> sqlite3ExprListDelete(db, pTable->pCheck);
> - if (pTable->def != NULL)
> + /* Do not delete pTable->def allocated not on region. */
> + assert(pTable->def != NULL);
> + if (pTable->def->opts.temporary == false)
5. Boolean variables checking on true/false can be omitted.
> @@ -603,29 +606,33 @@ sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
> static struct field_def *
> sql_field_retrieve(Parse *parser, Table *table, uint32_t id)
> {
> - sqlite3 *db = parser->db;
> struct field_def *field;
> assert(table->def != NULL);
> assert(id < SQLITE_MAX_COLUMN);
>
> if (id >= table->def->exact_field_count) {
> - uint32_t columns = table->def->exact_field_count;
> - columns = (columns > 0) ? 2 * columns : 1;
> - field = sqlite3DbRealloc(db, table->def->fields,
> - columns * sizeof(table->def->fields[0]));
> + uint32_t columns_new = table->def->exact_field_count;
> + columns_new = (columns_new > 0) ? 2 * columns_new : 1;
> + struct region *region = &fiber()->gc;
> + field = region_alloc(region,
> + columns_new * sizeof(table->def->fields[0]))
6. On region error please do diag_set error. Region error is Tarantool error, not
sqlite. And on such error you must return not SQLITE_NOMEM_BKPT, but
SQL_TARANTOOL_ERROR. It leads to returning to a user an error, that is set in diag.
> if (field == NULL) {
> parser->rc = SQLITE_NOMEM_BKPT;
> parser->nErr++;
> return NULL;
> }
>
> - for (uint32_t i = columns / 2; i < columns; i++) {
> + for (uint32_t i = 0; i < table->def->exact_field_count; i++) {
> + memcpy(&field[i], &table->def->fields[i],
> + sizeof(struct field_def));
> + }
7. It would be good to note here, that field names are not on the same
region allocation, and it is ok to memcpy pointers on them.
> + for (uint32_t i = columns_new / 2; i < columns_new; i++) {
> memcpy(&field[i], &field_def_default,
> sizeof(struct field_def));
> }
>
> table->def->fields = field;
> - table->def->exact_field_count = columns;
> + table->def->exact_field_count = columns_new;
8. This diff, and other columns->columns_new diff are garbage. Why did you
rename it? The previous name was ok.
> @@ -677,10 +685,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
> aNew =
> sqlite3DbRealloc(db, p->aCol,
> (p->def->field_count + 8) * sizeof(p->aCol[0]));
> - if (aNew == 0) {
> - sqlite3DbFree(db, z);
> + if (aNew == 0)
> return;
9. Please, use == NULL for pointers. I see, that it is left from the original
code, but if you modify it, then use Tarantool code style in the new code please.
> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> index 2ab8751..7dc14f6 100644
> --- a/src/box/sql/prepare.c
> +++ b/src/box/sql/prepare.c
> @@ -211,17 +211,19 @@ sqlite3InitDatabase(sqlite3 * db)
> void
> sqlite3ParserReset(Parse * pParse)
> {
> - if (pParse) {
> - sqlite3 *db = pParse->db;
> - sqlite3DbFree(db, pParse->aLabel);
> - sqlite3ExprListDelete(db, pParse->pConstExpr);
> - if (db) {
> - assert(db->lookaside.bDisable >=
> - pParse->disableLookaside);
> - db->lookaside.bDisable -= pParse->disableLookaside;
> - }
> - pParse->disableLookaside = 0;
> + if (pParse == NULL)
> + return;
> + sqlite3 *db = pParse->db;
> + sqlite3DbFree(db, pParse->aLabel);
> + sqlite3ExprListDelete(db, pParse->pConstExpr);
> + if (db) {
10. Same as 9.
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 9eeff8e..03bfcf9 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -1822,9 +1822,20 @@ sqlite3ColumnsFromExprList(Parse * pParse, /* Parsing context */
> aCol = 0;
> }
> assert(nCol == (i16) nCol);
> +
> + struct region *region = &fiber()->gc;
> assert(pTable->def->fields == NULL);
> + if (pTable->def->opts.temporary == false) {
11. At first, same as 5. At second, why? I can not see this in the original code.
> @@ -1877,9 +1888,16 @@ sqlite3ColumnsFromExprList(Parse * pParse, /* Parsing context */
> if (cnt > 3)
> sqlite3_randomness(sizeof(cnt), &cnt);
> }
> - pTable->def->fields[i].name = zName;
> - if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol) {
> + uint32_t zNameLen = (uint32_t)strlen(zName);
12. For new code please use Tarantool code style - no Camel case.
zNameLen -> name_len.
> + if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol)
13. Same as 9.
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 94d5c80..520b74c 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -2933,6 +2933,8 @@ struct Parse {
> u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
> u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
> u8 disableTriggers; /* True to disable triggers */
> + /** Region size at the Parser launch */
14. Please, put a dot at the end of the comment.
> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
> index c77aa9b..9055bd0 100644
> --- a/src/box/sql/tokenize.c
> +++ b/src/box/sql/tokenize.c
> @@ -664,6 +664,9 @@ sql_expr_compile(sqlite3 *db, const char *expr, struct Expr **result)
> memset(&parser, 0, sizeof(parser));
> parser.db = db;
> parser.parse_only = true;
> + struct region *region = &fiber()->gc;
> + parser.region_initial_size = region_used(region);
15. It is a second place, where a parser is created. How about creating a function
sql_parser_create(struct Parser *parser), that will do this?
16. I do not see, that you removed def rebuilding from sqlite3ColumnsFromExprList -
it is not needed anymore.
More information about the Tarantool-patches
mailing list