[tarantool-patches] Re: [PATCH v5 2/3] sql: remove SQL fields from Table and Column

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Fri May 11 23:59:45 MSK 2018


Hello. Thanks for the patch! See below 43 comments.

On 11/05/2018 11:49, Kirill Shcherbatov wrote:
> 1. Removed zName, is_nullable, collation, type
> from SQL Column.
> 2. Removed zColumns, zName from SQL Table.
> 3. Refactored Parser to use def_expression directly.
> 4. Introduced is_view flag.
> 4. Introduced sql_table_def_rebuild intended for collect
> fragmented with sql_field_retrieve space_def into memory
> located in one allocation.
> 
> Part of #3272, #3218.
> ---
>   src/box/field_def.h     |   6 +
>   src/box/space_def.c     |  29 +++--
>   src/box/sql.c           | 128 +++++++++++++++----
>   src/box/sql.h           |  33 +++++
>   src/box/sql/alter.c     |  55 +++++---
>   src/box/sql/analyze.c   |  16 ++-
>   src/box/sql/build.c     | 334 ++++++++++++++++++++++++++----------------------
>   src/box/sql/delete.c    |  21 +--
>   src/box/sql/expr.c      |  14 +-
>   src/box/sql/fkey.c      |  38 +++---
>   src/box/sql/hash.c      |   5 +-
>   src/box/sql/hash.h      |   2 +-
>   src/box/sql/insert.c    |  67 +++++-----
>   src/box/sql/pragma.c    |  34 +++--
>   src/box/sql/prepare.c   |  45 +++----
>   src/box/sql/resolve.c   |  22 ++--
>   src/box/sql/select.c    | 159 +++++++++++++----------
>   src/box/sql/sqliteInt.h |  37 ++++--
>   src/box/sql/tokenize.c  |   5 +-
>   src/box/sql/treeview.c  |   2 +-
>   src/box/sql/trigger.c   |   7 +-
>   src/box/sql/update.c    |  33 ++---
>   src/box/sql/util.c      |   9 --
>   src/box/sql/vdbe.c      |   2 +-
>   src/box/sql/vdbeaux.c   |   9 +-
>   src/box/sql/where.c     |  12 +-
>   src/box/sql/wherecode.c |   4 +-
>   src/box/sql/whereexpr.c |   6 +-
>   28 files changed, 675 insertions(+), 459 deletions(-)
> 
> diff --git a/src/box/field_def.h b/src/box/field_def.h
> index dfc1950..a42beab 100644
> --- a/src/box/field_def.h
> +++ b/src/box/field_def.h
> @@ -116,6 +116,12 @@ struct field_def {
>   	struct Expr *default_value_expr;
>   };
>   
> +static inline bool
> +nullable_action_to_is_nullable(enum on_conflict_action nullable_action)
> +{
> +	return nullable_action == ON_CONFLICT_ACTION_NONE;
> +}

1. Lets remove 'to': nullable_action_is_nullable. It is too long too, but I
can not think up another.
> diff --git a/src/box/space_def.c b/src/box/space_def.c
> index 22bd3ca..77c0e02 100644
> --- a/src/box/space_def.c
> +++ b/src/box/space_def.c
> @@ -116,12 +117,13 @@ space_def_dup(const struct space_def *src)
>   			if (src->fields[i].default_value != NULL) {
>   				ret->fields[i].default_value = strs_pos;
>   				strs_pos += strlen(strs_pos) + 1;
> -
> -				struct Expr *e =
> -					src->fields[i].default_value_expr;
> -				assert(e != NULL);
> +			}
> +			struct Expr *e =
> +				src->fields[i].default_value_expr;
> +			if (e != NULL) {
>   				char *expr_pos_old = expr_pos;
> -				e = sql_expr_dup(sql_get(), e, 0, &expr_pos);
> +				e = sql_expr_dup(sql_get(), e, 0,
> +						 &expr_pos);

2. Same as in the previous review: garbage diff.

> @@ -201,12 +203,13 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
>   				       fields[i].default_value, len);
>   				def->fields[i].default_value[len] = 0;
>   				strs_pos += len + 1;
> -
> -				struct Expr *e =
> -					fields[i].default_value_expr;
> -				assert(e != NULL);
> +			}
> +			struct Expr *e =
> +				fields[i].default_value_expr;
> +			if (e != NULL) {
>   				char *expr_pos_old = expr_pos;
> -				e = sql_expr_dup(sql_get(), e, 0, &expr_pos);
> +				e = sql_expr_dup(sql_get(), e, 0,
> +						 &expr_pos);

3. Same.
> diff --git a/src/box/sql.c b/src/box/sql.c
> index 166bb71..7d48cdc 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1681,3 +1697,65 @@ 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, const char *name)
> +{
> +	struct space_def *def = NULL;
> +	struct region *region = &fiber()->gc;
> +	size_t name_len = name != NULL ? strlen(name) : 0;
> +	size_t size = sizeof(struct space_def) + name_len + 1;

4. Use space_def_sizeof for this please.

> +	def = (struct space_def *)region_alloc(region, size);
> +	if (def == NULL) {
> +		diag_set(OutOfMemory, sizeof(struct tuple_dictionary),
> +			"region_alloc", "sql_ephemeral_space_def_new");
> +		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->nErr++;
> +		return NULL;
> +	} else {

5. If you return on def == NULL, then either move all the other code
under 'else' body, or remove the 'else'.

> +		memset(def, 0, size);
> +	}
> +	memcpy(def->name, name, name_len);
> +	def->name[name_len] = '\0';
> +	def->opts.temporary = true;
> +	return def;
> +}
> +
> diff --git a/src/box/sql.h b/src/box/sql.h
> index db92d80..ac8d07a 100644
> --- a/src/box/sql.h
> +++ b/src/box/sql.h
> @@ -65,6 +65,7 @@ sql_get();
>   struct Expr;
>   struct Parse;
>   struct Select;
> +struct Table;
>   
>   /**
>    * Perform parsing of provided expression. This is done by
> @@ -143,6 +144,38 @@ sql_expr_dup(struct sqlite3 *db, struct Expr *p, int flags, char **buffer);
>   void
>   sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
>   
> +/**
> + * Create and initialize a new ephemeric SQL Table object.

6. Same as in the previous review - ephemeric -> ephemeral.

> + * @param parser SQL Parser object.
> + * @param name Table to create name.
> + * @retval NULL on memory allocation error, Parser state changed.
> + * @retval not NULL on success.
> + */
> +struct Table *
> +sql_ephemeral_table_new(struct Parse *parser, const char *name);
> +
> +/**
> + * Create and initialize a new ephemeric space_def object.

7. Same.

> + * @param parser SQL Parser object.
> + * @param name Table to create name.
> + * @retval NULL on memory allocation error, Parser state changed.
> + * @retval not NULL on success.
> + */
> +struct space_def *
> +sql_ephemeral_space_def_new(struct Parse *parser, const char *name);
> +
> +/**
> + * Rebuild struct def in Table with memory allocated on a single
> + * malloc. Fields and strings are expected to be allocated with
> + * sqlite3DbMalloc.

8. In the function body: "/* All allocations are on region. */".
Please, fix the comment.
> diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
> index 24f0965..85404ac 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -195,7 +199,12 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
>   				"Cannot add a REFERENCES column with non-NULL default value");
>   		return;
>   	}
> -	if (pCol->notNull && !pDflt) {
> +	assert(pNew->def->fields[pNew->def->field_count - 1].is_nullable ==
> +	       nullable_action_to_is_nullable(
> +		pNew->def->fields[pNew->def->field_count - 1].nullable_action));
> +
> +	if (pNew->def->fields[pNew->def->field_count - 1].nullable_action

9. Please, do not use non-boolean variables as booleans.
Use (action != ON_CONFLICT_ACTION_NONE) instead of just (action).

> +	    && !pDflt) {

10. pDflt == NULL.
> @@ -281,25 +293,30 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
>   	pNew = (Table *) sqlite3DbMallocZero(db, sizeof(Table));
>   	if (!pNew)
>   		goto exit_begin_add_column;
> +	pNew->def = space_def_dup(pTab->def);
> +	if (pNew->def == NULL) {

11. Forgot to set sqlite3OomFault(db). I know, that this code is
unreachable, but in the future it will be not.
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index a2b712a..a02fe89 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -291,15 +291,8 @@ sqlite3CommitInternalChanges()
>   void
>   sqlite3DeleteColumnNames(sqlite3 * db, Table * pTable)
>   {
> -	int i;
> -	Column *pCol;
>   	assert(pTable != 0);
> -	if ((pCol = pTable->aCol) != 0) {
> -		for (i = 0; i < pTable->nCol; i++, pCol++) {
> -			sqlite3DbFree(db, pCol->zName);
> -		}
> -		sqlite3DbFree(db, pTable->aCol);
> -	}
> +	sqlite3DbFree(db, pTable->aCol);

12. Nice. Now this function consists of a single DbFree - lets just
inline it and remove sqlite3DeleteColumnNames.

> @@ -494,31 +484,19 @@ sqlite3PrimaryKeyIndex(Table * pTab)
>   
>   /**
>    * Create and initialize a new SQL Table object.
> + * All memory is allocated on region.

13. Not all - the table itself is on the heap.

> @@ -626,30 +605,35 @@ 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(table->def->exact_field_count >= (uint32_t)table->nCol);
>   	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]));
>   		if (field == NULL) {
> -			parser->rc = SQLITE_NOMEM_BKPT;
> +			diag_set(OutOfMemory, columns_new * sizeof(table->def->fields[0]),
> +				"region_alloc", "sql_field_retrieve");

14. Out of 80 symbols.

> +			parser->rc = SQL_TARANTOOL_ERROR;
>   			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));

15. Why you can not do one memcpy(field, table->def->fields,
				sizeof(*field) * table->def->exact_field_count); ?

> +		}
> +		for (uint32_t i = columns_new / 2; i < columns_new; i++) {
>   			memcpy(&field[i], &field_def_default,
>   			       sizeof(struct field_def));
> @@ -675,42 +659,44 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
>   	sqlite3 *db = pParse->db;
>   	if ((p = pParse->pNewTable) == 0)
>   		return;
> +	assert(p->def->opts.temporary);
>   #if SQLITE_MAX_COLUMN
> -	if (p->nCol + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
> -		sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
> +	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
> +		sqlite3ErrorMsg(pParse, "too many columns on %s",
> +				p->def->name);
>   		return;
>   	}
>   #endif
> -	if (sql_field_retrieve(pParse, p, (uint32_t) p->nCol) == NULL)
> +	if (sql_field_retrieve(pParse, p, (uint32_t) p->def->field_count) == NULL)

16. Out of 80 symbols.

>   		return;
> -	z = sqlite3DbMallocRaw(db, pName->n + 1);
> +	struct region *region = &fiber()->gc;
> +	z = region_alloc(region, pName->n + 1);
>   	if (z == 0)

17. Missed diag_set + pParse->nErr + pParse->rc.

> @@ -756,9 +741,11 @@ sqlite3AddNotNull(Parse * pParse, int onError)
>   {
>   	Table *p;
>   	p = pParse->pNewTable;
> -	if (p == 0 || NEVER(p->nCol < 1))
> +	if (p == 0 || NEVER(p->def->field_count < 1))
>   		return;
> -	p->aCol[p->nCol - 1].notNull = (u8) onError;
> +	p->def->fields[p->def->field_count - 1].nullable_action = (u8)onError;
> +	p->def->fields[p->def->field_count - 1].is_nullable =
> +		(onError == ON_CONFLICT_ACTION_NONE);

18. Now you have nullable_action_to_is_nullable.
> @@ -871,38 +858,31 @@ void
>   sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
>   {
>   	Table *p;
> -	Column *pCol;
>   	sqlite3 *db = pParse->db;
>   	p = pParse->pNewTable;
> +	assert(p->def->opts.temporary);
>   	if (p != 0) {
> -		pCol = &(p->aCol[p->nCol - 1]);
>   		if (!sqlite3ExprIsConstantOrFunction
>   		    (pSpan->pExpr, db->init.busy)) {
>   			sqlite3ErrorMsg(pParse,
>   					"default value of column [%s] is not constant",
> -					pCol->zName);
> +					p->def->fields[p->def->field_count - 1].name);
>   		} else {
> -			/* A copy of pExpr is used instead of the original, as pExpr contains
> -			 * tokens that point to volatile memory. The 'span' of the expression
> -			 * is required by pragma table_info.
> -			 */
> -			Expr x;
>   			assert(p->def != NULL);
>   			struct field_def *field =
> -				&p->def->fields[p->nCol - 1];
> -			sql_expr_free(db, field->default_value_expr, false);
> -
> -			memset(&x, 0, sizeof(x));
> -			x.op = TK_SPAN;
> -			x.u.zToken = sqlite3DbStrNDup(db, (char *)pSpan->zStart,
> -						      (int)(pSpan->zEnd -
> -							    pSpan->zStart));
> -			x.pLeft = pSpan->pExpr;
> -			x.flags = EP_Skip;
> -
> -			field->default_value_expr =
> -				sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
> -			sqlite3DbFree(db, x.u.zToken);
> +				&p->def->fields[p->def->field_count - 1];
> +			struct region *region = &fiber()->gc;
> +			uint32_t default_length = (int)(pSpan->zEnd - pSpan->zStart);
> +			field->default_value = region_alloc(region,
> +							    default_length + 1);
> +			if (field->default_value == NULL) {

19. Lets use diag + SQL_TARANTOOL_ERROR.

> +				pParse->rc = SQLITE_NOMEM_BKPT;
> +				pParse->nErr++;
> +				return;
> +			}
> +			strncpy(field->default_value, (char *)pSpan->zStart,

20. Why do you case to char*? zStart is already const char* and strncpy takes it ok.
> @@ -1087,8 +1067,10 @@ sql_column_collation(Table *table, uint32_t column)
>   	 * SQL specific structures.
>   	 */
>   	if (space == NULL || space_index(space, 0) == NULL) {
> -		assert(column < (uint32_t)table->nCol);
> -		return table->aCol[column].coll;
> +		assert(column < (uint32_t)table->def->field_count);
> +		struct coll *coll =
> +			coll_by_id(table->def->fields[column].coll_id);
> +		return coll;

21. It is simpler and shorter to return coll_by_id() with no saving
into the variable.
> @@ -2149,11 +2142,16 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
>   			 * normally holds CHECK constraints on an ordinary table, but for
>   			 * a VIEW it holds the list of column names.
>   			 */
> -			sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
> -						   &pTable->nCol,
> -						   &pTable->aCol);
> +			sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);
> +			struct space_def *old_def = pTable->def;
> +			/* Delete it manually. */
> +			old_def->opts.temporary = true;
> +			if (sql_table_def_rebuild(db, pTable) != 0)
> +				nErr++;
> +			space_def_delete(old_def);

22. On error pTable->def is not reset, so here you would delete actually
old pTable->def, that is on a region. Do this space_def_delete in 'else' branch.
> @@ -2163,12 +2161,31 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
>   			 * the column names from the SELECT statement that defines the view.
>   			 */
>   			assert(pTable->aCol == 0);
> -			pTable->nCol = pSelTab->nCol;
> +			assert((int)pTable->def->field_count == -1);
> +			assert(pSelTab->def->opts.temporary);
> +
> +			struct space_def *old_def = pTable->def;
> +			struct space_def *new_def = NULL;

23. Useless initialization - you reset this variable on the next line.

> +			new_def = sql_ephemeral_space_def_new(pParse, old_def->name);

24. Out of 80 symbols.

> +			if (new_def == NULL) {
> +				nErr++;
> +			} else {
> +				memcpy(new_def, old_def, sizeof(struct space_def));

25. Same.

> +				new_def->dict = NULL;
> +				new_def->opts.temporary = true;
> +				new_def->fields = pSelTab->def->fields;
> +				new_def->field_count = pSelTab->def->field_count;

26. Same.
> @@ -2193,10 +2210,22 @@ sqliteViewResetAll(sqlite3 * db)
>   	for (i = sqliteHashFirst(&db->pSchema->tblHash); i;
>   	     i = sqliteHashNext(i)) {
>   		Table *pTab = sqliteHashData(i);
> -		if (pTab->pSelect) {
> +		assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
> +		if (pTab->def->opts.is_view) {
>   			sqlite3DeleteColumnNames(db, pTab);
> +			struct space_def *old_def = pTab->def;
> +			assert(old_def->opts.temporary == false);
> +			/* Ignore fields allocated on region */

27. As I can see, here pTab is the normal view, that has normal
non-temporary space_def on malloc. Not on region.

> +			pTab->def = space_def_new(old_def->id, old_def->uid,
> +						  0, old_def->name,
> +						  strlen(old_def->name),
> +						  old_def->engine_name,
> +						  strlen(old_def->engine_name),
> +						  &old_def->opts,
> +						  NULL, 0);
> +			assert(pTab->def);

28. It is not guaranteed. You must check for def == NULL and set
sqlite3OomFault(db). And do not reset pTab def, if you can not create a new one.
> @@ -2966,7 +2996,9 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
>   	 */
>   	if (pList == 0) {
>   		Token prevCol;
> -		sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol - 1].zName);
> +		sqlite3TokenInit(&prevCol,
> +				 pTab->def->
> +					 fields[pTab->def->field_count - 1].name);

29. Please, do not wrap '->'. And it is out of 80 symbols anyway.
> diff --git a/src/box/sql/hash.c b/src/box/sql/hash.c
> index cedcb7d..b109cca 100644
> --- a/src/box/sql/hash.c
> +++ b/src/box/sql/hash.c
> @@ -69,6 +69,7 @@ sqlite3HashClear(Hash * pH)
>   	while (elem) {
>   		HashElem *next_elem = elem->next;
>   		sqlite3_free(elem);
> +		free(elem->pKey);
>   		elem = next_elem;
>   	}
>   	pH->count = 0;
> @@ -292,7 +293,7 @@ sqlite3HashInsert(Hash * pH, const char *pKey, void *data)
>   			removeElementGivenHash(pH, elem, h);
>   		} else {
>   			elem->data = data;
> -			elem->pKey = pKey;
> +			elem->pKey = strdup(pKey);

30. strdup can fail. See the sqlite3HashInsert what to do in such a case.
> @@ -301,7 +302,7 @@ sqlite3HashInsert(Hash * pH, const char *pKey, void *data)
>   	new_elem = (HashElem *) sqlite3Malloc(sizeof(HashElem));
>   	if (new_elem == 0)
>   		return data;
> -	new_elem->pKey = pKey;
> +	new_elem->pKey = strdup(pKey);

31. Same.
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 939b5e3..c272ae1 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -146,13 +146,14 @@ sqlite3TableAffinity(Vdbe * v, Table * pTab, int iReg)
>   	char *zColAff = pTab->zColAff;
>   	if (zColAff == 0) {
>   		sqlite3 *db = sqlite3VdbeDb(v);
> -		zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol + 1);
> +		zColAff = (char *)sqlite3DbMallocRaw(0,
> +						     pTab->def->field_count + 1);

32. Out of 80.
> @@ -611,10 +612,10 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
>   		ipkColumn = pTab->iPKey;
>   	}
>   
> -	if (pColumn == 0 && nColumn && nColumn != (pTab->nCol)) {
> +	if (pColumn == 0 && nColumn && nColumn != ((int)pTab->def->field_count)) {

33. Same.
> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> index 2ab8751..926c3dd 100644
> --- a/src/box/sql/prepare.c
> +++ b/src/box/sql/prepare.c
> @@ -456,3 +435,21 @@ sqlite3_prepare_v2(sqlite3 * db,	/* Database handle. */
>   	assert(rc == SQLITE_OK || ppStmt == 0 || *ppStmt == 0);	/* VERIFY: F13021 */
>   	return rc;
>   }
> +
> +void
> +sql_parser_free(Parse *parser)
> +{
> +	if (parser == NULL)
> +		return;

34. It is never == NULL as I can see.

> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 5a50413..32a8e08 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -1661,14 +1661,15 @@ columnTypeImpl(NameContext * pNC, Expr * pExpr,
>   			} else if (pTab->pSchema) {
>   				/* A real table */
>   				assert(!pS);
> -				assert(iCol >= 0 && iCol < pTab->nCol);
> +				assert(iCol >= 0 && iCol <
> +						    (int)pTab->def->field_count);

35. Out of 80.
> @@ -1819,11 +1818,25 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>   		testcase(aCol == 0);
>   	} else {
>   		nCol = 0;
> -		aCol = 0;
> +		aCol = NULL;
>   	}
>   	assert(nCol == (i16) nCol);
> -	*pnCol = nCol;
> -	*paCol = aCol;
> +
> +	/*
> +	 * This should be a table without resolved columns.
> +	 * sqlite3ViewGetColumnNames could use it to resolve
> +	 * names for existent table.

36. existent -> existing.

> +	 */
> +	assert(pTable->def->fields == NULL);

37. Lets better check def->opts.is_temporary. field == NULL means nothing.

> +	struct region *region = &fiber()->gc;
> +	pTable->def->fields =
> +		region_alloc(region, nCol * sizeof(pTable->def->fields[0]));

38. region_alloc can fail - set
diag + pParse->rc = SQL_TARANTOOL_ERROR + pParse->nErr++;

or

sqlite3OomFault().

Unfortunately we still have no standard way to handle OOM in SQL.
> @@ -1874,22 +1887,28 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>   			if (cnt > 3)
>   				sqlite3_randomness(sizeof(cnt), &cnt);
>   		}
> -		pCol->zName = zName;
> -		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol) {
> +		uint32_t name_len = (uint32_t)strlen(zName);
> +		if (zName != NULL && sqlite3HashInsert(&ht, zName, pCol) == pCol)
>   			sqlite3OomFault(db);
> +		pTable->def->fields[i].name =
> +			region_alloc(region, name_len + 1);
> +		if (pTable->def->fields[i].name == NULL) {
> +			sqlite3OomFault(db);
> +		} else {
> +			memcpy(pTable->def->fields[i].name, zName, name_len);
> +			pTable->def->fields[i].name[name_len] = '\0';
>   		}
>   	}
>   	sqlite3HashClear(&ht);
> -	if (db->mallocFailed) {
> -		for (j = 0; j < i; j++) {
> -			sqlite3DbFree(db, aCol[j].zName);
> -		}
> +	int rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_OK;
> +	if (rc != SQLITE_OK) {
>   		sqlite3DbFree(db, aCol);
> -		*paCol = 0;
> -		*pnCol = 0;
> -		return SQLITE_NOMEM_BKPT;
> +		pTable->def->fields = NULL;
> +		pTable->def->field_count = 0;

39. pTable->def is on region and is not deleted together with table. You
can skip this cleaning.
> @@ -4683,18 +4701,24 @@ selectExpander(Walker * pWalker, Select * p)
>   			assert(pFrom->pTab == 0);
>   			if (sqlite3WalkSelect(pWalker, pSel))
>   				return WRC_Abort;
> +			/* Will overwritten with pointer as unique identifier. */

40. Out of 66.

41. Will *be* overwritten.

> +			const char *name = "sqlite_sq_DEADBEAFDEADBEAF";
>   			pFrom->pTab = pTab =
> -			    sqlite3DbMallocZero(db, sizeof(Table));
> -			if (pTab == 0)
> +				sql_ephemeral_table_new(pParse, name);
> +			if (pTab == NULL)
>   				return WRC_Abort;
> +			/* rewrite old name with correct pointer */

42. Same in the previous review - please, start a sentence from a capital
letter, and finish with dot.
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 8bb45c9..4fba008 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -1867,15 +1867,6 @@ struct Savepoint {
>    * of this structure.
>    */
>   struct Column {
> -	char *zName;		/* Name of this column */
> -	enum field_type type;	/* Column type. */
> -	/** Collating sequence. */
> -	struct coll *coll;
> -	/**
> -	 * An ON_CONFLICT_ACTION code for handling a NOT NULL
> -	 * constraint.
> -	 */
> -	enum on_conflict_action notNull;

Niceeee.
> @@ -4153,4 +4142,24 @@ table_column_nullable_action(struct Table *tab, uint32_t column);
>   bool
>   table_column_is_nullable(struct Table *tab, uint32_t column);
>   
> +/**
> + * Initialize a new parser object.
> + * @param parser object to initialize.
> + */
> +static inline void
> +sql_parser_create(struct Parse *parser)
> +{
> +	memset(parser, 0, PARSE_HDR_SZ);
> +	memset(PARSE_TAIL(parser), 0, PARSE_TAIL_SZ);
> +	struct region *region = &fiber()->gc;
> +	parser->region_initial_size = region_used(region);
> +}
> +
> +/**
> + * Release the parser object resources.
> + * @param parser object to release.
> + */
> +void
> +sql_parser_free(struct Parse *parser);

43. Out convention is 'create/destroy'. Please, respect it.




More information about the Tarantool-patches mailing list