[Tarantool-patches] [PATCH] sql: extend result set with span
Nikita Pettik
korablev at tarantool.org
Mon Dec 30 02:59:16 MSK 2019
On 30 Dec 01:32, Vladislav Shpilevoy wrote:
> LGTM.
Pushed to master.
> On 29/12/2019 01:56, Nikita Pettik wrote:
> > Each column of result set features its name span (in full metadata
> > mode). For instance:
> >
> > SELECT x + 1 AS add FROM ...;
> >
> > In this case real name (span) of resulting set column is "x + 1"
> > meanwhile "add" is its alias. This patch extends metadata with
> > member which corresponds to column's original expression.
> > It is worth mentioning that in most cases span coincides with name, so
> > to avoid overhead and sending the same string twice, we follow the rule
> > that if span is encoded as MP_NIL then its value is the same as name.
> > Also note that span is always presented in full metadata mode.
> >
> > Closes #4407
> >
> > @TarantoolBot document
> > Title: extended SQL metadata
> >
> > Before this patch metadata for SQL DQL contained only two fields:
> > name and type of each column of result set. Now it may contain
> > following properties:
> > - collation (in case type of resulting set column is string and
> > collation is different from default "none");
> > is encoded with IPROTO_FIELD_COLL (0x2) key in IPROTO_METADATA map;
> > in msgpack is encoded as string and held with MP_STR type;
> > - is_nullable (in case column of result set corresponds to space's
> > field; for expressions like x+1 for the sake of
> > simplicity nullability is omitted);
> > is encoded with IPROTO_FIELD_IS_NULLABLE key (0x3) in IPROTO_METADATA;
> > in msgpack is encoded as boolean and held with MP_BOOL type;
> > note that absence of this field implies that nullability is unknown;
> > - is_autoincrement (is set only for autoincrement column in result
> > set);
> > is encoded with IPROTO_FIELD_IS_AUNTOINCREMENT (0x4) key in IPROTO_METADATA;
> > in msgpack is encoded as boolean and held with MP_BOOL type;
> > - span (is always set in full metadata mode; it is an original
> > expression forming result set column. For instance:
> > SELECT a + 1 AS x; -- x is a name, meanwhile a + 1 is a span);
> > is encoded with IPROTO_FIELD_SPAN (0x5) key in IPROTO_METADATA map;
> > in msgpack is encoded as string and held with MP_STR type OR
> > as NIL with MP_NIL type. The latter case indicates that span
> > coincides with name. This simple optimization allows to avoid
> > sending the same string twice.
> >
> > This extended metadata is send only when PRAGMA full_metadata is
> > enabled. Otherwise, only basic (name and type) metadata is processed.
> > ---
> > Branch: https://github.com/tarantool/tarantool/tree/np/gh-4407-extend-sql-metadata-v2
> > Issue: https://github.com/tarantool/tarantool/issues/4407
> >
> > src/box/execute.c | 33 ++++++++++++--
> > src/box/iproto_constants.h | 1 +
> > src/box/lua/execute.c | 10 +++++
> > src/box/lua/net_box.c | 34 ++++++++++----
> > src/box/sql/select.c | 10 +++++
> > src/box/sql/sqlInt.h | 6 +++
> > src/box/sql/vdbe.h | 3 ++
> > src/box/sql/vdbeInt.h | 6 +++
> > src/box/sql/vdbeapi.c | 15 +++++++
> > src/box/sql/vdbeaux.c | 19 ++++++++
> > test/sql/full_metadata.result | 99 ++++++++++++++++++++++++++++++++++++++---
> > test/sql/full_metadata.test.lua | 11 +++++
> > 12 files changed, 230 insertions(+), 17 deletions(-)
> >
> > diff --git a/src/box/execute.c b/src/box/execute.c
> > index c70935d01..3034cee86 100644
> > --- a/src/box/execute.c
> > +++ b/src/box/execute.c
> > @@ -270,7 +270,7 @@ error:
> >
> > static inline size_t
> > metadata_map_sizeof(const char *name, const char *type, const char *coll,
> > - int nullable, bool is_autoincrement)
> > + const char *span, int nullable, bool is_autoincrement)
> > {
> > uint32_t members_count = 2;
> > size_t map_size = 0;
> > @@ -289,6 +289,12 @@ metadata_map_sizeof(const char *name, const char *type, const char *coll,
> > map_size += mp_sizeof_uint(IPROTO_FIELD_IS_AUTOINCREMENT);
> > map_size += mp_sizeof_bool(true);
> > }
> > + if (sql_metadata_is_full()) {
> > + members_count++;
> > + map_size += mp_sizeof_uint(IPROTO_FIELD_SPAN);
> > + map_size += span != NULL ? mp_sizeof_str(strlen(span)) :
> > + mp_sizeof_nil();
> > + }
> > map_size += mp_sizeof_uint(IPROTO_FIELD_NAME);
> > map_size += mp_sizeof_uint(IPROTO_FIELD_TYPE);
> > map_size += mp_sizeof_str(strlen(name));
> > @@ -299,10 +305,13 @@ metadata_map_sizeof(const char *name, const char *type, const char *coll,
> >
> > static inline void
> > metadata_map_encode(char *buf, const char *name, const char *type,
> > - const char *coll, int nullable, bool is_autoincrement)
> > + const char *coll, const char *span, int nullable,
> > + bool is_autoincrement)
> > {
> > uint32_t map_sz = 2 + (coll != NULL) + (nullable != -1) +
> > is_autoincrement;
> > + if (sql_metadata_is_full())
> > + map_sz++;
> > buf = mp_encode_map(buf, map_sz);
> > buf = mp_encode_uint(buf, IPROTO_FIELD_NAME);
> > buf = mp_encode_str(buf, name, strlen(name));
> > @@ -320,6 +329,21 @@ metadata_map_encode(char *buf, const char *name, const char *type,
> > buf = mp_encode_uint(buf, IPROTO_FIELD_IS_AUTOINCREMENT);
> > buf = mp_encode_bool(buf, true);
> > }
> > + if (sql_metadata_is_full()) {
> > + /*
> > + * Span is an original expression that forms
> > + * result set column. In most cases it is the
> > + * same as column name. So to avoid sending
> > + * the same string twice simply encode it as
> > + * a nil and account this behaviour on client
> > + * side (see decode_metadata_optional()).
> > + */
> > + buf = mp_encode_uint(buf, IPROTO_FIELD_SPAN);
> > + if (span != NULL)
> > + buf = mp_encode_str(buf, span, strlen(span));
> > + else
> > + buf = mp_encode_nil(buf);
> > + }
> > }
> >
> > /**
> > @@ -348,6 +372,7 @@ sql_get_metadata(struct sql_stmt *stmt, struct obuf *out, int column_count)
> > const char *coll = sql_column_coll(stmt, i);
> > const char *name = sql_column_name(stmt, i);
> > const char *type = sql_column_datatype(stmt, i);
> > + const char *span = sql_column_span(stmt, i);
> > int nullable = sql_column_nullable(stmt, i);
> > bool is_autoincrement = sql_column_is_autoincrement(stmt, i);
> > /*
> > @@ -357,14 +382,14 @@ sql_get_metadata(struct sql_stmt *stmt, struct obuf *out, int column_count)
> > */
> > assert(name != NULL);
> > assert(type != NULL);
> > - size = metadata_map_sizeof(name, type, coll, nullable,
> > + size = metadata_map_sizeof(name, type, coll, span, nullable,
> > is_autoincrement);
> > char *pos = (char *) obuf_alloc(out, size);
> > if (pos == NULL) {
> > diag_set(OutOfMemory, size, "obuf_alloc", "pos");
> > return -1;
> > }
> > - metadata_map_encode(pos, name, type, coll, nullable,
> > + metadata_map_encode(pos, name, type, coll, span, nullable,
> > is_autoincrement);
> > }
> > return 0;
> > diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h
> > index 30d1af4cb..3808c6f28 100644
> > --- a/src/box/iproto_constants.h
> > +++ b/src/box/iproto_constants.h
> > @@ -134,6 +134,7 @@ enum iproto_metadata_key {
> > IPROTO_FIELD_COLL = 2,
> > IPROTO_FIELD_IS_NULLABLE = 3,
> > IPROTO_FIELD_IS_AUTOINCREMENT = 4,
> > + IPROTO_FIELD_SPAN = 5,
> > };
> >
> > enum iproto_ballot_key {
> > diff --git a/src/box/lua/execute.c b/src/box/lua/execute.c
> > index e8e3e2a9f..5ed7b9bd3 100644
> > --- a/src/box/lua/execute.c
> > +++ b/src/box/lua/execute.c
> > @@ -23,10 +23,13 @@ lua_sql_get_metadata(struct sql_stmt *stmt, struct lua_State *L,
> > const char *coll = sql_column_coll(stmt, i);
> > const char *name = sql_column_name(stmt, i);
> > const char *type = sql_column_datatype(stmt, i);
> > + const char *span = sql_column_span(stmt, i);
> > int nullable = sql_column_nullable(stmt, i);
> > bool is_autoincrement = sql_column_is_autoincrement(stmt, i);
> > size_t table_sz = 2 + (coll != NULL) + (nullable != -1) +
> > is_autoincrement;
> > + if (sql_metadata_is_full())
> > + table_sz++;
> > lua_createtable(L, 0, table_sz);
> > /*
> > * Can not fail, since all column names are
> > @@ -51,6 +54,13 @@ lua_sql_get_metadata(struct sql_stmt *stmt, struct lua_State *L,
> > lua_pushboolean(L, true);
> > lua_setfield(L, -2, "is_autoincrement");
> > }
> > + if (sql_metadata_is_full()) {
> > + if (span != NULL)
> > + lua_pushstring(L, span);
> > + else
> > + lua_pushstring(L, name);
> > + lua_setfield(L, -2, "span");
> > + }
> > lua_rawseti(L, -2, i + 1);
> > }
> > }
> > diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c
> > index c22848874..6db506b57 100644
> > --- a/src/box/lua/net_box.c
> > +++ b/src/box/lua/net_box.c
> > @@ -641,7 +641,7 @@ netbox_decode_select(struct lua_State *L)
> > /** Decode optional (i.e. may be present in response) metadata fields. */
> > static void
> > decode_metadata_optional(struct lua_State *L, const char **data,
> > - uint32_t map_size)
> > + uint32_t map_size, const char *name, uint32_t name_len)
> > {
> > /* 2 is default metadata map size (field name + field size). */
> > while (map_size-- > 2) {
> > @@ -655,6 +655,24 @@ decode_metadata_optional(struct lua_State *L, const char **data,
> > bool is_nullable = mp_decode_bool(data);
> > lua_pushboolean(L, is_nullable);
> > lua_setfield(L, -2, "is_nullable");
> > + } else if (key == IPROTO_FIELD_SPAN) {
> > + /*
> > + * There's an agreement: if span is not
> > + * presented in metadata (encoded as NIL),
> > + * then it is the same as name. It allows
> > + * avoid sending the same string twice.
> > + */
> > + const char *span = NULL;
> > + if (mp_typeof(**data) == MP_STR) {
> > + span = mp_decode_str(data, &len);
> > + } else {
> > + assert(mp_typeof(**data) == MP_NIL);
> > + mp_decode_nil(data);
> > + span = name;
> > + len = name_len;
> > + }
> > + lua_pushlstring(L, span, len);
> > + lua_setfield(L, -2, "span");
> > } else {
> > assert(key == IPROTO_FIELD_IS_AUTOINCREMENT);
> > bool is_autoincrement = mp_decode_bool(data);
> > @@ -676,21 +694,21 @@ netbox_decode_metadata(struct lua_State *L, const char **data)
> > lua_createtable(L, count, 0);
> > for (uint32_t i = 0; i < count; ++i) {
> > uint32_t map_size = mp_decode_map(data);
> > - assert(map_size >= 2 && map_size <= 5);
> > + assert(map_size >= 2 && map_size <= 6);
> > uint32_t key = mp_decode_uint(data);
> > assert(key == IPROTO_FIELD_NAME);
> > (void) key;
> > lua_createtable(L, 0, map_size);
> > - uint32_t len;
> > - const char *str = mp_decode_str(data, &len);
> > - lua_pushlstring(L, str, len);
> > + uint32_t name_len, type_len;
> > + const char *str = mp_decode_str(data, &name_len);
> > + lua_pushlstring(L, str, name_len);
> > lua_setfield(L, -2, "name");
> > key = mp_decode_uint(data);
> > assert(key == IPROTO_FIELD_TYPE);
> > - const char *type = mp_decode_str(data, &len);
> > - lua_pushlstring(L, type, len);
> > + const char *type = mp_decode_str(data, &type_len);
> > + lua_pushlstring(L, type, type_len);
> > lua_setfield(L, -2, "type");
> > - decode_metadata_optional(L, data, map_size);
> > + decode_metadata_optional(L, data, map_size, str, name_len);
> > lua_rawseti(L, -2, i + 1);
> > }
> > }
> > diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> > index a19494ed9..b4182b88a 100644
> > --- a/src/box/sql/select.c
> > +++ b/src/box/sql/select.c
> > @@ -1849,6 +1849,10 @@ generate_column_metadata(struct Parse *pParse, struct SrcList *pTabList,
> > if (space->sequence != NULL &&
> > space->sequence_fieldno == (uint32_t) iCol)
> > vdbe_metadata_set_col_autoincrement(v, i);
> > + if (pEList->a[i].zName != NULL) {
> > + vdbe_metadata_set_col_span(v, i,
> > + pEList->a[i].zSpan);
> > + }
> > }
> > } else {
> > const char *z = NULL;
> > @@ -1859,6 +1863,12 @@ generate_column_metadata(struct Parse *pParse, struct SrcList *pTabList,
> > else
> > z = tt_sprintf("column%d", i + 1);
> > vdbe_metadata_set_col_name(v, i, z);
> > + if (is_full_meta) {
> > + if (pEList->a[i].zName != NULL) {
> > + vdbe_metadata_set_col_span(v, i,
> > + pEList->a[i].zSpan);
> > + }
> > + }
> > }
> > }
> > if (var_count == 0)
> > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> > index e248bc673..250f6a2cd 100644
> > --- a/src/box/sql/sqlInt.h
> > +++ b/src/box/sql/sqlInt.h
> > @@ -536,6 +536,9 @@ sql_finalize(sql_stmt * pStmt);
> > int
> > sql_reset(struct sql_stmt *stmt);
> >
> > +bool
> > +sql_metadata_is_full();
> > +
> > int
> > sql_exec(sql *, /* An open database */
> > const char *sql, /* SQL to be evaluated */
> > @@ -585,6 +588,9 @@ sql_column_nullable(sql_stmt *stmt, int n);
> > bool
> > sql_column_is_autoincrement(sql_stmt *stmt, int n);
> >
> > +const char *
> > +sql_column_span(sql_stmt *stmt, int n);
> > +
> > int
> > sql_initialize(void);
> >
> > diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
> > index 1e585d89d..79fe6f95d 100644
> > --- a/src/box/sql/vdbe.h
> > +++ b/src/box/sql/vdbe.h
> > @@ -263,6 +263,9 @@ vdbe_metadata_set_col_nullability(struct Vdbe *p, int idx, int nullable);
> > void
> > vdbe_metadata_set_col_autoincrement(struct Vdbe *p, int idx);
> >
> > +int
> > +vdbe_metadata_set_col_span(struct Vdbe *p, int idx, const char *span);
> > +
> > void sqlVdbeCountChanges(Vdbe *);
> > sql *sqlVdbeDb(Vdbe *);
> > void sqlVdbeSetSql(Vdbe *, const char *z, int n, int);
> > diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
> > index fcdcd9b70..1022ec040 100644
> > --- a/src/box/sql/vdbeInt.h
> > +++ b/src/box/sql/vdbeInt.h
> > @@ -357,6 +357,12 @@ struct sql_column_metadata {
> > int8_t nullable;
> > /** True if column features autoincrement property. */
> > bool is_actoincrement;
> > + /**
> > + * Span is an original expression forming result set
> > + * column. In most cases it is the same as name; it is
> > + * different only in case of presence of AS clause.
> > + */
> > + char *span;
> > };
> >
> > /*
> > diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
> > index 5b423c9df..559251140 100644
> > --- a/src/box/sql/vdbeapi.c
> > +++ b/src/box/sql/vdbeapi.c
> > @@ -36,6 +36,7 @@
> > */
> > #include "sqlInt.h"
> > #include "vdbeInt.h"
> > +#include "box/session.h"
> >
> > /*
> > * Invoke the profile callback. This routine is only called if we already
> > @@ -115,6 +116,12 @@ sql_clear_bindings(sql_stmt * pStmt)
> > return rc;
> > }
> >
> > +bool
> > +sql_metadata_is_full()
> > +{
> > + return current_session()->sql_flags & SQL_FullMetadata;
> > +}
> > +
> > /**************************** sql_value_ ******************************
> > * The following routines extract information from a Mem or sql_value
> > * structure.
> > @@ -769,6 +776,14 @@ sql_column_is_autoincrement(sql_stmt *stmt, int n)
> > return p->metadata[n].is_actoincrement;
> > }
> >
> > +const char *
> > +sql_column_span(sql_stmt *stmt, int n)
> > +{
> > + struct Vdbe *p = (struct Vdbe *) stmt;
> > + assert(n < sql_column_count(stmt) && n >= 0);
> > + return p->metadata[n].span;
> > +}
> > +
> > /******************************* sql_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 5b4bc0182..d4db6aca2 100644
> > --- a/src/box/sql/vdbeaux.c
> > +++ b/src/box/sql/vdbeaux.c
> > @@ -1835,6 +1835,7 @@ vdbe_metadata_delete(struct Vdbe *v)
> > free(v->metadata[i].name);
> > free(v->metadata[i].type);
> > free(v->metadata[i].collation);
> > + free(v->metadata[i].span);
> > }
> > free(v->metadata);
> > }
> > @@ -1921,6 +1922,24 @@ vdbe_metadata_set_col_autoincrement(struct Vdbe *p, int idx)
> > p->metadata[idx].is_actoincrement = true;
> > }
> >
> > +int
> > +vdbe_metadata_set_col_span(struct Vdbe *p, int idx, const char *span)
> > +{
> > + assert(idx < p->nResColumn);
> > + if (p->metadata[idx].span != NULL)
> > + free((void *)p->metadata[idx].span);
> > + if (span == NULL) {
> > + p->metadata[idx].span = NULL;
> > + return 0;
> > + }
> > + p->metadata[idx].span = strdup(span);
> > + if (p->metadata[idx].span == NULL) {
> > + diag_set(OutOfMemory, strlen(span) + 1, "strdup", "span");
> > + return -1;
> > + }
> > + return 0;
> > +}
> > +
> > /*
> > * This routine checks that the sql.nVdbeActive count variable
> > * matches the number of vdbe's in the list sql.pVdbe that are
> > diff --git a/test/sql/full_metadata.result b/test/sql/full_metadata.result
> > index 463108001..ca69f4145 100644
> > --- a/test/sql/full_metadata.result
> > +++ b/test/sql/full_metadata.result
> > @@ -56,6 +56,7 @@ execute("SELECT 'aSd' COLLATE \"unicode_ci\";")
> > | ---
> > | - metadata:
> > | - type: string
> > + | span: '''aSd'' COLLATE "unicode_ci"'
> > | name: '''aSd'' COLLATE "unicode_ci"'
> > | collation: unicode_ci
> > | rows:
> > @@ -64,7 +65,8 @@ execute("SELECT 'aSd' COLLATE \"unicode_ci\";")
> > execute("SELECT c FROM t;")
> > | ---
> > | - metadata:
> > - | - type: string
> > + | - span: C
> > + | type: string
> > | is_nullable: true
> > | name: C
> > | collation: unicode_ci
> > @@ -75,6 +77,7 @@ execute("SELECT c COLLATE \"unicode\" FROM t;")
> > | ---
> > | - metadata:
> > | - type: string
> > + | span: c COLLATE "unicode"
> > | name: c COLLATE "unicode"
> > | collation: unicode
> > | rows:
> > @@ -86,14 +89,17 @@ execute("SELECT c COLLATE \"unicode\" FROM t;")
> > execute("SELECT id, a, c FROM t;")
> > | ---
> > | - metadata:
> > - | - type: integer
> > + | - span: ID
> > + | type: integer
> > | is_autoincrement: true
> > | name: ID
> > | is_nullable: false
> > | - type: integer
> > + | span: A
> > | name: A
> > | is_nullable: false
> > - | - type: string
> > + | - span: C
> > + | type: string
> > | is_nullable: true
> > | name: C
> > | collation: unicode_ci
> > @@ -103,14 +109,17 @@ execute("SELECT id, a, c FROM t;")
> > execute("SELECT * FROM t;")
> > | ---
> > | - metadata:
> > - | - type: integer
> > + | - span: ID
> > + | type: integer
> > | is_autoincrement: true
> > | name: ID
> > | is_nullable: false
> > | - type: integer
> > + | span: A
> > | name: A
> > | is_nullable: false
> > - | - type: string
> > + | - span: C
> > + | type: string
> > | is_nullable: true
> > | name: C
> > | collation: unicode_ci
> > @@ -118,6 +127,83 @@ execute("SELECT * FROM t;")
> > | - [1, 1, 'aSd']
> > | ...
> >
> > +-- Span is always set in extended metadata. Span is an original
> > +-- expression forming result set column.
> > +--
> > +execute("SELECT 1 AS x;")
> > + | ---
> > + | - metadata:
> > + | - type: integer
> > + | span: '1'
> > + | name: X
> > + | rows:
> > + | - [1]
> > + | ...
> > +execute("SELECT *, id + 1 AS x, a AS y, c || 'abc' FROM t;")
> > + | ---
> > + | - metadata:
> > + | - span: ID
> > + | type: integer
> > + | is_autoincrement: true
> > + | name: ID
> > + | is_nullable: false
> > + | - type: integer
> > + | span: A
> > + | name: A
> > + | is_nullable: false
> > + | - span: C
> > + | type: string
> > + | is_nullable: true
> > + | name: C
> > + | collation: unicode_ci
> > + | - type: integer
> > + | span: id + 1
> > + | name: X
> > + | - type: integer
> > + | span: a
> > + | name: Y
> > + | is_nullable: false
> > + | - type: string
> > + | span: c || 'abc'
> > + | name: c || 'abc'
> > + | rows:
> > + | - [1, 1, 'aSd', 2, 1, 'aSdabc']
> > + | ...
> > +
> > +box.execute("CREATE VIEW v AS SELECT id + 1 AS x, a AS y, c || 'abc' FROM t;")
> > + | ---
> > + | - row_count: 1
> > + | ...
> > +execute("SELECT * FROM v;")
> > + | ---
> > + | - metadata:
> > + | - type: integer
> > + | span: X
> > + | name: X
> > + | - type: integer
> > + | span: Y
> > + | name: Y
> > + | is_nullable: false
> > + | - type: string
> > + | span: c || 'abc'
> > + | name: c || 'abc'
> > + | rows:
> > + | - [2, 1, 'aSdabc']
> > + | ...
> > +execute("SELECT x, y FROM v;")
> > + | ---
> > + | - metadata:
> > + | - type: integer
> > + | span: x
> > + | name: X
> > + | - type: integer
> > + | span: y
> > + | name: Y
> > + | is_nullable: false
> > + | rows:
> > + | - [2, 1]
> > + | ...
> > +
> > execute("PRAGMA full_metadata = false;")
> > | ---
> > | - row_count: 0
> > @@ -139,6 +225,9 @@ test_run:cmd("setopt delimiter ''");
> > | - true
> > | ...
> >
> > +box.space.V:drop()
> > + | ---
> > + | ...
> > box.space.T:drop()
> > | ---
> > | ...
> > diff --git a/test/sql/full_metadata.test.lua b/test/sql/full_metadata.test.lua
> > index e3b30f7e7..576c49ad3 100644
> > --- a/test/sql/full_metadata.test.lua
> > +++ b/test/sql/full_metadata.test.lua
> > @@ -35,6 +35,16 @@ execute("SELECT c COLLATE \"unicode\" FROM t;")
> > execute("SELECT id, a, c FROM t;")
> > execute("SELECT * FROM t;")
> >
> > +-- Span is always set in extended metadata. Span is an original
> > +-- expression forming result set column.
> > +--
> > +execute("SELECT 1 AS x;")
> > +execute("SELECT *, id + 1 AS x, a AS y, c || 'abc' FROM t;")
> > +
> > +box.execute("CREATE VIEW v AS SELECT id + 1 AS x, a AS y, c || 'abc' FROM t;")
> > +execute("SELECT * FROM v;")
> > +execute("SELECT x, y FROM v;")
> > +
> > execute("PRAGMA full_metadata = false;")
> >
> > test_run:cmd("setopt delimiter ';'")
> > @@ -45,4 +55,5 @@ if remote then
> > end;
> > test_run:cmd("setopt delimiter ''");
> >
> > +box.space.V:drop()
> > box.space.T:drop()
> >
More information about the Tarantool-patches
mailing list