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

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Wed Feb 28 22:36:52 MSK 2018


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.

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)




More information about the Tarantool-patches mailing list