[patches] [PATCH 5/7] sql: store column meta in a special structure instead of Mem

Konstantin Osipov kostja at tarantool.org
Thu Mar 29 17:37:28 MSK 2018


* Vladislav Shpilevoy <v.shpilevoy at tarantool.org> [18/02/28 22:41]:
> Struct Mem is a too common data structure to store column meta.
> Original SQLite stores column names, table names, column type
> in separate structs Mem. And to get other metadata like
> nullability, belonging to a primary key, autoincrement flag it
> is necessary to lookup struct Table by name, lookup column in a
> table by a column name, and do this steps for each result set
> column.
> 
> To speed up meta info looking up, decrease memory usage and
> increase count of characteristics store meta in a special
> structure that stores all needed values on creation.

This patch does many things, mainly removes some obsolete code. 
I fail to grasp all of them, since they are all bundled together.

I wonder why you need a separate data structure is needed.
Ideally, you should have all the metadata available in the AST, so
you should simply iterate over Expr objects in select-list of
the AST to get it.

You of course could create an intermediate structure to store
iteration results, like sql_column_meta, but given that Expr now
has become part of tarantool core, I don't think it's worth the
hassle.

Please push the cleanups separately and try to avoid
unnecessary fuss with copying data to a separate data structure.

> 
> Needed for #2620
> 
> Signed-off-by: Vladislav Shpilevoy <v.shpilevoy at tarantool.org>
> ---
>  src/box/sql/delete.c    |   4 +-
>  src/box/sql/insert.c    |   4 +-
>  src/box/sql/legacy.c    |   3 -
>  src/box/sql/pragma.c    |   8 +--
>  src/box/sql/prepare.c   |   6 +-
>  src/box/sql/select.c    | 143 ++++++++++++++++++++----------------------------
>  src/box/sql/sqlite3.h   |  13 ++++-
>  src/box/sql/sqliteInt.h |  44 +++++++++++++++
>  src/box/sql/update.c    |   4 +-
>  src/box/sql/vdbe.h      |   5 +-
>  src/box/sql/vdbeInt.h   |   3 +-
>  src/box/sql/vdbeapi.c   |  35 ++++--------
>  src/box/sql/vdbeaux.c   | 114 ++++++++++++++++++++++++--------------
>  13 files changed, 218 insertions(+), 168 deletions(-)
> 
> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> index 832af89fd..6f3d3f881 100644
> --- a/src/box/sql/delete.c
> +++ b/src/box/sql/delete.c
> @@ -581,8 +581,8 @@ sqlite3DeleteFrom(Parse * pParse,	/* The parser context */
>  	    !pParse->nested && !pParse->pTriggerTab) {
>  		sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
>  		sqlite3VdbeSetNumCols(v, 1);
> -		sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted",
> -				      SQLITE_STATIC);
> +		sqlite3VdbeSetColMeta(v, 0, "rows deleted", SQLITE_STATIC, NULL,
> +				      0);
>  	}
>  
>   delete_from_cleanup:
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index b20a47970..ac11a5f34 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -906,8 +906,8 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
>  	    && !pParse->pTriggerTab) {
>  		sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
>  		sqlite3VdbeSetNumCols(v, 1);
> -		sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted",
> -				      SQLITE_STATIC);
> +		sqlite3VdbeSetColMeta(v, 0, "rows inserted", SQLITE_STATIC,
> +				      NULL, 0);
>  	}
>  
>   insert_cleanup:
> diff --git a/src/box/sql/legacy.c b/src/box/sql/legacy.c
> index e75709551..ed3ead5db 100644
> --- a/src/box/sql/legacy.c
> +++ b/src/box/sql/legacy.c
> @@ -113,9 +113,6 @@ sqlite3_exec(sqlite3 * db,	/* The database on which the SQL executes */
>  						    (char *)
>  						    sqlite3_column_name(pStmt,
>  									i);
> -						/* sqlite3VdbeSetColName() installs column names as UTF8
> -						 * strings so there is no way for sqlite3_column_name() to fail.
> -						 */
>  						assert(azCols[i] != 0);
>  					}
>  					callbackIsInit = 1;
> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
> index 40c6e5302..85aaf42d5 100644
> --- a/src/box/sql/pragma.c
> +++ b/src/box/sql/pragma.c
> @@ -128,13 +128,13 @@ setPragmaResultColumnNames(Vdbe * v,	/* The query under construction */
>  	u8 n = pPragma->nPragCName;
>  	sqlite3VdbeSetNumCols(v, n == 0 ? 1 : n);
>  	if (n == 0) {
> -		sqlite3VdbeSetColName(v, 0, COLNAME_NAME, pPragma->zName,
> -				      SQLITE_STATIC);
> +		sqlite3VdbeSetColMeta(v, 0, pPragma->zName, SQLITE_STATIC, NULL,
> +				      0);
>  	} else {
>  		int i, j;
>  		for (i = 0, j = pPragma->iPragCName; i < n; i++, j++) {
> -			sqlite3VdbeSetColName(v, i, COLNAME_NAME, pragCName[j],
> -					      SQLITE_STATIC);
> +			sqlite3VdbeSetColMeta(v, i, pragCName[j], SQLITE_STATIC,
> +					      NULL, 0);
>  		}
>  	}
>  }
> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> index 36965a727..60d4de43c 100644
> --- a/src/box/sql/prepare.c
> +++ b/src/box/sql/prepare.c
> @@ -434,9 +434,9 @@ sqlite3Prepare(sqlite3 * db,	/* Database handle. */
>  			mx = 8;
>  		}
>  		for (i = iFirst; i < mx; i++) {
> -			sqlite3VdbeSetColName(sParse.pVdbe, i - iFirst,
> -					      COLNAME_NAME, azColName[i],
> -					      SQLITE_STATIC);
> +			sqlite3VdbeSetColMeta(sParse.pVdbe, i - iFirst,
> +					      azColName[i], SQLITE_STATIC, NULL,
> +					      0);
>  		}
>  	}
>  #endif
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 660423d76..8dad5cb32 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -1569,35 +1569,36 @@ generateSortTail(Parse * pParse,	/* Parsing context */
>  	sqlite3VdbeResolveLabel(v, addrBreak);
>  }
>  
> -/*
> - * Return a pointer to a string containing the 'declaration type' of the
> - * expression pExpr. The string may be treated as static by the caller.
> - *
> - * Also try to estimate the size of the returned value and return that
> - * result in *pEstWidth.
> - *
> - * The declaration type is the exact datatype definition extracted from the
> - * original CREATE TABLE statement if the expression is a column.
> - * Exactly when an expression is considered a column can be complex
> - * in the presence of subqueries. The result-set expression in all
> - * of the following SELECT statements is considered a column by this function.
> - *
> - *   SELECT col FROM tbl;
> - *   SELECT (SELECT col FROM tbl;
> - *   SELECT (SELECT col FROM tbl);
> - *   SELECT abc FROM (SELECT col AS abc FROM tbl);
> - *
> - * The declaration type for any expression other than a column is NULL.
> +/**
> + * Get a declaration type of an expression and its estimated size.
> + * The result string may be treated as static by the caller.
> + *
> + * The declaration type is the exact datatype definition extracted
> + * from the original CREATE TABLE statement if the expression is a
> + * column. Exactly when an expression is considered a column can
> + * be complex in the presence of subqueries. The result-set
> + * expression in all of the following SELECT statements is
> + * considered a column by this function. Only table columns has
> + * declaration type - for others NULL is returned.
> + * @param pNC Parser context with tables list.
> + * @param pExpr Expression to detect type.
> + * @param[out] table Table containing the found column. Can be
> + *             NULL.
> + * @param[out] fieldno Index of a column in a table. Can be NULL.
> + * @param[out] pEstWidth Estimated column value size. Can be NULL.
> + *
> + * @retval not NULL Table column declaration type.
> + * @retval     NULL An expression is not column.
>   */
>  static const char *
> -columnType(NameContext *pNC, Expr *pExpr, const char **pzOrigTab,
> -	   const char **pzOrigCol, u8 *pEstWidth)
> +columnType(NameContext *pNC, Expr *pExpr, const struct Table **table,
> +	   uint32_t *fieldno, u8 *pEstWidth)
>  {
>  	char const *zType = 0;
>  	int j;
>  	u8 estWidth = 1;
> -	char const *zOrigTab = 0;
> -	char const *zOrigCol = 0;
> +	const struct Table *found_table = NULL;
> +	uint32_t found_fieldno = 0;
>  
>  	assert(pExpr != 0);
>  	assert(pNC->pSrcList != 0);
> @@ -1667,17 +1668,18 @@ columnType(NameContext *pNC, Expr *pExpr, const char **pzOrigTab,
>  					sNC.pNext = pNC;
>  					sNC.pParse = pNC->pParse;
>  					zType =
> -					    columnType(&sNC, p, &zOrigTab,
> -						       &zOrigCol, &estWidth);
> +					    columnType(&sNC, p, &found_table,
> +						       &found_fieldno,
> +						       &estWidth);
>  				}
>  			} else if (pTab->pSchema) {
>  				/* A real table */
>  				assert(!pS);
>  				assert(iCol >= 0 && iCol < pTab->nCol);
> -				zOrigCol = pTab->aCol[iCol].zName;
> +				found_fieldno = iCol;
>  				zType = sqlite3ColumnType(&pTab->aCol[iCol], 0);
>  				estWidth = pTab->aCol[iCol].szEst;
> -				zOrigTab = pTab->zName;
> +				found_table = pTab;
>  			}
>  			break;
>  		}
> @@ -1695,51 +1697,23 @@ columnType(NameContext *pNC, Expr *pExpr, const char **pzOrigTab,
>  			sNC.pNext = pNC;
>  			sNC.pParse = pNC->pParse;
>  			zType =
> -			    columnType(&sNC, p, &zOrigTab, &zOrigCol,
> +			    columnType(&sNC, p, &found_table, &found_fieldno,
>  				       &estWidth);
>  			break;
>  		}
>  #endif
>  	}
>  
> -	if (pzOrigTab) {
> -		assert(pzOrigTab && pzOrigCol);
> -		*pzOrigTab = zOrigTab;
> -		*pzOrigCol = zOrigCol;
> +	if (table != NULL) {
> +		assert(table != NULL && fieldno != NULL);
> +		*table = found_table;
> +		*fieldno = found_fieldno;
>  	}
>  	if (pEstWidth)
>  		*pEstWidth = estWidth;
>  	return zType;
>  }
>  
> -/*
> - * Generate code that will tell the VDBE the declaration types of columns
> - * in the result set.
> - */
> -static void
> -generateColumnTypes(Parse * pParse,	/* Parser context */
> -		    SrcList * pTabList,	/* List of tables */
> -		    ExprList * pEList)	/* Expressions defining the result set */
> -{
> -	Vdbe *v = pParse->pVdbe;
> -	int i;
> -	NameContext sNC;
> -	sNC.pSrcList = pTabList;
> -	sNC.pParse = pParse;
> -	for (i = 0; i < pEList->nExpr; i++) {
> -		Expr *p = pEList->a[i].pExpr;
> -		const char *zType;
> -		const char *zOrigTab = 0;
> -		const char *zOrigCol = 0;
> -		zType = columnType(&sNC, p, &zOrigTab, &zOrigCol, 0);
> -
> -		sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab,
> -				      SQLITE_TRANSIENT);
> -		sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol,
> -				      SQLITE_TRANSIENT);
> -	}
> -}
> -
>  /*
>   * Generate code that will tell the VDBE the names of columns
>   * in the result set.  This information is used to provide the
> @@ -1770,16 +1744,22 @@ generateColumnNames(Parse * pParse,	/* Parser context */
>  	pParse->colNamesSet = 1;
>  	fullNames = (user_session->sql_flags & SQLITE_FullColNames) != 0;
>  	shortNames = (user_session->sql_flags & SQLITE_ShortColNames) != 0;
> +	NameContext sNC;
> +	sNC.pSrcList = pTabList;
> +	sNC.pParse = pParse;
>  	sqlite3VdbeSetNumCols(v, pEList->nExpr);
>  	for (i = 0; i < pEList->nExpr; i++) {
> -		Expr *p;
> -		p = pEList->a[i].pExpr;
> +		const struct Table *table;
> +		uint32_t fieldno;
> +		char *alias;
> +		void *destructor;
> +		Expr *p = pEList->a[i].pExpr;
>  		if (NEVER(p == 0))
>  			continue;
> +		columnType(&sNC, p, &table, &fieldno, NULL);
>  		if (pEList->a[i].zName) {
> -			char *zName = pEList->a[i].zName;
> -			sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName,
> -					      SQLITE_TRANSIENT);
> +			alias = pEList->a[i].zName;
> +			destructor = SQLITE_TRANSIENT;
>  		} else if (p->op == TK_COLUMN || p->op == TK_AGG_COLUMN) {
>  			Table *pTab;
>  			char *zCol;
> @@ -1795,31 +1775,26 @@ generateColumnNames(Parse * pParse,	/* Parser context */
>  			assert(iCol >= 0 && iCol < pTab->nCol);
>  			zCol = pTab->aCol[iCol].zName;
>  			if (!shortNames && !fullNames) {
> -				sqlite3VdbeSetColName(v, i, COLNAME_NAME,
> -						      sqlite3DbStrDup(db,
> -								      pEList->a[i].zSpan),
> -						      SQLITE_DYNAMIC);
> +				alias = sqlite3DbStrDup(db, pEList->a[i].zSpan);
> +				destructor = SQLITE_DYNAMIC;
>  			} else if (fullNames) {
> -				char *zName = 0;
> -				zName =
> -				    sqlite3MPrintf(db, "%s.%s", pTab->zName,
> -						   zCol);
> -				sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName,
> -						      SQLITE_DYNAMIC);
> +				alias = sqlite3MPrintf(db, "%s.%s", pTab->zName,
> +						       zCol);
> +				destructor = SQLITE_DYNAMIC;
>  			} else {
> -				sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol,
> -						      SQLITE_TRANSIENT);
> +				alias = zCol;
> +				destructor = SQLITE_TRANSIENT;
>  			}
>  		} else {
> -			const char *z = pEList->a[i].zSpan;
> -			z = z == 0 ? sqlite3MPrintf(db, "column%d",
> -						    i + 1) : sqlite3DbStrDup(db,
> -									     z);
> -			sqlite3VdbeSetColName(v, i, COLNAME_NAME, z,
> -					      SQLITE_DYNAMIC);
> +			alias = pEList->a[i].zSpan;
> +			if (alias == NULL)
> +				alias = sqlite3MPrintf(db, "column%d", i + 1);
> +			else
> +				alias = sqlite3DbStrDup(db, alias);
> +			destructor = SQLITE_DYNAMIC;
>  		}
> +		sqlite3VdbeSetColMeta(v, i, alias, destructor, table, fieldno);
>  	}
> -	generateColumnTypes(pParse, pTabList, pEList);
>  }
>  
>  /*
> diff --git a/src/box/sql/sqlite3.h b/src/box/sql/sqlite3.h
> index ab0c8761a..90a77e5df 100644
> --- a/src/box/sql/sqlite3.h
> +++ b/src/box/sql/sqlite3.h
> @@ -3925,12 +3925,19 @@ sqlite3_column_name(sqlite3_stmt *, int N);
>   * occurs.  ^Otherwise, they return the name of the attached database, table,
>   * or column that query result column was extracted from.
>   */
> -SQLITE_API const char *
> -sqlite3_column_table_name(sqlite3_stmt *, int);
> -
>  SQLITE_API const char *
>  sqlite3_column_origin_name(sqlite3_stmt *, int);
>  
> +/**
> + * Get column meta information.
> + * @param stmt Prepared statement.
> + * @param fieldno Field number of a column to get meta of.
> + * @return Column meta.
> + */
> +struct sql_column_meta;
> +const struct sql_column_meta *
> +sqlite3_column_meta(sqlite3_stmt *stmt, int fieldno);
> +
>  /*
>   * CAPI3REF: Evaluate An SQL Statement
>   * METHOD: sqlite3_stmt
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index aebb61029..3258c8edc 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -1359,6 +1359,50 @@ struct Column {
>  	u8 colFlags;		/* Boolean properties.  See COLFLAG_ defines below */
>  };
>  
> +/**
> + * Column meta information, generated on DML/SQL requests. On DDL
> + * operations no columns and no meta.
> + * On DML column meta is restricted by "rows inserted",
> + * "rows deleted" and other aggregated information that in general
> + * does not depend on a request type.
> + * On DQL (SELECT) each column has a meta. A result set column can
> + * be directly reflected to a column of a table, or be a result of
> + * an expression, or be a constant.
> + * If a column does not belong to a table, then meta stores only
> + * its alias (SELECT ... as <alias> ...). If a column belongs to
> + * a table, then before a request execution in a meta some info
> + * is aggregated.
> + * Meta can not simply store direct pointers to a Table or Column
> + * objects, since in a future we will add iterators, prepared
> + * statements and other things, that must survive after DDL.
> + * Moreover Vinyl engine can yield in DQL, and during a yield DDL
> + * is possible too.
> + */
> +struct sql_column_meta {
> +	/** Column name, visible to a user. */
> +	char *alias;
> +	/**
> +	 * Alias can be reference to a static memory - do not free
> +	 * it in such a case.
> +	 */
> +	bool need_free_alias;
> +	/**
> +	 * Original column name stored in a table, if a column
> +	 * belongs to a table. Else NULL.
> +	 */
> +	char *name;
> +	union {
> +		/** Some meta, aggregated for a table column. */
> +		struct PACKED {
> +			bool is_nullable : 1;
> +			bool is_primary_part : 1;
> +			bool is_autoincrement : 1;
> +			bool is_case_sensitive : 1;
> +		};
> +		uint8_t flags;
> +	};
> +};
> +
>  /* Allowed values for Column.colFlags:
>   */
>  #define COLFLAG_PRIMKEY  0x0001	/* Column is part of the primary key */
> diff --git a/src/box/sql/update.c b/src/box/sql/update.c
> index 85d18cbde..aa53c426f 100644
> --- a/src/box/sql/update.c
> +++ b/src/box/sql/update.c
> @@ -688,8 +688,8 @@ sqlite3Update(Parse * pParse,		/* The parser context */
>  	    !pParse->pTriggerTab && !pParse->nested) {
>  		sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
>  		sqlite3VdbeSetNumCols(v, 1);
> -		sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated",
> -				      SQLITE_STATIC);
> +		sqlite3VdbeSetColMeta(v, 0, "rows updated", SQLITE_STATIC, NULL,
> +				      0);
>  	}
>  
>   update_cleanup:
> diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
> index e66e4f9bf..7c49a67bd 100644
> --- a/src/box/sql/vdbe.h
> +++ b/src/box/sql/vdbe.h
> @@ -233,7 +233,10 @@ void sqlite3VdbeResetStepResult(Vdbe *);
>  void sqlite3VdbeRewind(Vdbe *);
>  int sqlite3VdbeReset(Vdbe *);
>  void sqlite3VdbeSetNumCols(Vdbe *, int);
> -int sqlite3VdbeSetColName(Vdbe *, int, int, const char *, void (*)(void *));
> +void
> +sqlite3VdbeSetColMeta(Vdbe *p, int idx,
> +		      const char *alias, void *destructor,
> +		      const struct Table *space, uint32_t fieldno);
>  void sqlite3VdbeCountChanges(Vdbe *);
>  sqlite3 *sqlite3VdbeDb(Vdbe *);
>  void sqlite3VdbeSetSql(Vdbe *, const char *z, int n, int);
> diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
> index 1f7fdd37b..b3c8acf17 100644
> --- a/src/box/sql/vdbeInt.h
> +++ b/src/box/sql/vdbeInt.h
> @@ -406,7 +406,8 @@ struct Vdbe {
>  	Op *aOp;		/* Space to hold the virtual machine's program */
>  	Mem *aMem;		/* The memory locations */
>  	Mem **apArg;		/* Arguments to currently executing user function */
> -	Mem *aColName;		/* Column names to return */
> +	/* Column meta to return. */
> +	struct sql_column_meta *columns;
>  	Mem *pResultSet;	/* Pointer to an array of results */
>  	char *zErrMsg;		/* Error message written here */
>  	VdbeCursor **apCsr;	/* One element of this array for each open cursor */
> diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
> index d995a534b..2d0efbda0 100644
> --- a/src/box/sql/vdbeapi.c
> +++ b/src/box/sql/vdbeapi.c
> @@ -1166,18 +1166,10 @@ columnName(sqlite3_stmt *pStmt, int N, int useType)
>  	assert(db != NULL);
>  	n = sqlite3_column_count(pStmt);
>  	if (N < n && N >= 0) {
> -		N += useType * n;
> -		sqlite3_mutex_enter(db->mutex);
> -		assert(db->mallocFailed == 0);
> -		ret = (const char *) sqlite3_value_text(&p->aColName[N]);
> -		/* A malloc may have failed inside of the xFunc() call. If this
> -		 * is the case, clear the mallocFailed flag and return NULL.
> -		 */
> -		if (db->mallocFailed) {
> -			sqlite3OomClear(db);
> -			ret = NULL;
> -		}
> -		sqlite3_mutex_leave(db->mutex);
> +		if (useType == COLNAME_NAME)
> +			ret = p->columns[N].alias;
> +		else
> +			ret = p->columns[N].name;
>  	}
>  	return ret;
>  }
> @@ -1192,17 +1184,6 @@ sqlite3_column_name(sqlite3_stmt * pStmt, int N)
>  	return columnName(pStmt, N, COLNAME_NAME);
>  }
>  
> -/*
> - * Return the name of the table from which a result column derives.
> - * NULL is returned if the result column is an expression or constant or
> - * anything else which is not an unambiguous reference to a database column.
> - */
> -const char *
> -sqlite3_column_table_name(sqlite3_stmt * pStmt, int N)
> -{
> -	return columnName(pStmt, N, COLNAME_TABLE);
> -}
> -
>  /*
>   * Return the name of the table column from which a result column derives.
>   * NULL is returned if the result column is an expression or constant or
> @@ -1214,6 +1195,14 @@ sqlite3_column_origin_name(sqlite3_stmt * pStmt, int N)
>  	return columnName(pStmt, N, COLNAME_COLUMN);
>  }
>  
> +const struct sql_column_meta *
> +sqlite3_column_meta(sqlite3_stmt *stmt, int fieldno)
> +{
> +	Vdbe *p = (Vdbe *) stmt;
> +	assert(fieldno < p->nResColumn);
> +	return &p->columns[fieldno];
> +}
> +
>  /******************************* sqlite3_bind_  **************************
>   *
>   * Routines used to attach values to wildcards in a compiled SQL statement.
> diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
> index d7546ab43..9d13a6449 100644
> --- a/src/box/sql/vdbeaux.c
> +++ b/src/box/sql/vdbeaux.c
> @@ -38,6 +38,7 @@
>  #include "box/schema.h"
>  #include "box/tuple_format.h"
>  #include "box/txn.h"
> +#include "box/coll_cache.h"
>  #include "msgpuck/msgpuck.h"
>  #include "sqliteInt.h"
>  #include "vdbeInt.h"
> @@ -2370,51 +2371,85 @@ Cleanup(Vdbe * p)
>   * be called on an SQL statement before sqlite3_step().
>   */
>  void
> -sqlite3VdbeSetNumCols(Vdbe * p, int nResColumn)
> +sqlite3VdbeSetNumCols(Vdbe *p, int n)
>  {
> -	int n;
>  	sqlite3 *db = p->db;
> -
> -	releaseMemArray(p->aColName, p->nResColumn * COLNAME_N);
> -	sqlite3DbFree(db, p->aColName);
> -	n = nResColumn * COLNAME_N;
> -	p->nResColumn = (u16) nResColumn;
> -	p->aColName = (Mem *) sqlite3DbMallocRawNN(db, sizeof(Mem) * n);
> -	if (p->aColName == 0)
> -		return;
> -	initMemArray(p->aColName, n, p->db, MEM_Null);
> +	struct sql_column_meta *col = p->columns;
> +	for (uint32_t i = 0; i < p->nResColumn; ++i, ++col) {
> +		if (col->need_free_alias)
> +			sqlite3DbFree(db, col->alias);
> +		sqlite3DbFree(db, col->name);
> +		col->alias = NULL;
> +		col->name = NULL;
> +	}
> +	if (n == 0) {
> +		sqlite3DbFree(db, p->columns);
> +	} else {
> +		uint32_t size = sizeof(p->columns[0]) * n;
> +		struct sql_column_meta *new_meta = (struct sql_column_meta *)
> +			sqlite3DbRealloc(db, p->columns, size);
> +		if (new_meta == NULL)
> +			return;
> +		p->columns = new_meta;
> +		memset(new_meta, 0, size);
> +	}
> +	p->nResColumn = (u16) n;
>  }
>  
> -/*
> - * Set the name of the idx'th column to be returned by the SQL statement.
> - * zName must be a pointer to a nul terminated string.
> - *
> - * This call must be made after a call to sqlite3VdbeSetNumCols().
> - *
> - * The final parameter, xDel, must be one of SQLITE_DYNAMIC, SQLITE_STATIC
> - * or SQLITE_TRANSIENT. If it is SQLITE_DYNAMIC, then the buffer pointed
> - * to by zName will be freed by sqlite3DbFree() when the vdbe is destroyed.
> +/**
> + * Store column meta in VDBE.
> + * @param p VDBE to store in.
> + * @param idx Column 0-based index.
> + * @param alias Alias visible to a user.
> + * @param destructor Type of an alias: it can be static memory,
> + *        memory that must be duplicated, and already duplicated.
> + * @param table Table containing a column, or NULL.
> + * @param fieldno Column number in a table.
>   */
> -int
> -sqlite3VdbeSetColName(Vdbe * p,			/* Vdbe being configured */
> -		      int idx,			/* Index of column zName applies to */
> -		      int var,			/* One of the COLNAME_* constants */
> -		      const char *zName,	/* Pointer to buffer containing name */
> -		      void (*xDel) (void *))	/* Memory management strategy for zName */
> -{
> -	int rc;
> -	Mem *pColName;
> +void
> +sqlite3VdbeSetColMeta(Vdbe *p, int idx,
> +		      const char *alias, void *destructor,
> +		      const struct Table *table, uint32_t fieldno)
> +{
>  	assert(idx < p->nResColumn);
> -	assert(var < COLNAME_N);
> -	if (p->db->mallocFailed) {
> -		assert(!zName || xDel != SQLITE_DYNAMIC);
> -		return SQLITE_NOMEM_BKPT;
> +	struct sql_column_meta *meta = &p->columns[idx];
> +	if (table != NULL) {
> +		assert(fieldno < (uint32_t) table->nCol);
> +		const struct Column *column = &table->aCol[fieldno];
> +		meta->name = sqlite3DbStrDup(p->db, column->zName);
> +		meta->is_nullable = column->notNull == ON_CONFLICT_ACTION_NONE;
> +		meta->is_primary_part =
> +			(column->colFlags & COLFLAG_PRIMKEY) != 0;
> +		meta->is_autoincrement =
> +			meta->is_primary_part &&
> +			(table->tabFlags & TF_Autoincrement) != 0;
> +		if (column->zColl != NULL) {
> +			struct coll *coll = coll_by_name(column->zColl,
> +							 strlen(column->zColl));
> +			if (coll != NULL) {
> +				meta->is_case_sensitive =
> +					coll_is_case_sensitive(coll);
> +			} else {
> +				meta->is_case_sensitive = true;
> +			}
> +		} else {
> +			meta->is_case_sensitive = true;
> +		}
> +		if (column->zName == alias) {
> +			/*
> +			 * If a column is selected with no alias,
> +			 * then do not copy it twice. Store two
> +			 * pointers to the same name.
> +			 */
> +			destructor = SQLITE_STATIC;
> +			alias = meta->name;
> +		}
>  	}
> -	assert(p->aColName != 0);
> -	pColName = &(p->aColName[idx + var * p->nResColumn]);
> -	rc = sqlite3VdbeMemSetStr(pColName, zName, -1, 1, xDel);
> -	assert(rc != 0 || !zName || (pColName->flags & MEM_Term) != 0);
> -	return rc;
> +	if (destructor == SQLITE_TRANSIENT)
> +		meta->alias = sqlite3DbStrDup(p->db, alias);
> +	else
> +		meta->alias = (char *) alias;
> +	meta->need_free_alias = destructor != SQLITE_STATIC;
>  }
>  
>  /*
> @@ -2999,7 +3034,6 @@ sqlite3VdbeClearObject(sqlite3 * db, Vdbe * p)
>  {
>  	SubProgram *pSub, *pNext;
>  	assert(p->db == 0 || p->db == db);
> -	releaseMemArray(p->aColName, p->nResColumn * COLNAME_N);
>  	for (pSub = p->pProgram; pSub; pSub = pNext) {
>  		pNext = pSub->pNext;
>  		vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
> @@ -3011,7 +3045,7 @@ sqlite3VdbeClearObject(sqlite3 * db, Vdbe * p)
>  		sqlite3DbFree(db, p->pFree);
>  	}
>  	vdbeFreeOpArray(db, p->aOp, p->nOp);
> -	sqlite3DbFree(db, p->aColName);
> +	sqlite3VdbeSetNumCols(p, 0);
>  	sqlite3DbFree(db, p->zSql);
>  #ifdef SQLITE_ENABLE_STMT_SCANSTATUS
>  	{
> -- 
> 2.14.3 (Apple Git-98)

-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov



More information about the Tarantool-patches mailing list