[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