[Tarantool-patches] [PATCH 4/6] sql: extend result set with nullability

Nikita Pettik korablev at tarantool.org
Wed Nov 27 15:15:44 MSK 2019


If member of result set is (solely) column identifier, then metadata
will contain its corresponding field nullability as boolean property.
Note that indicating nullability for other expressions (like x + 1)
may make sense but it requires derived nullability calculation which in
turn seems to be overkill (at least in scope of current patch).

Part of #4407
---
 src/box/execute.c                                |  18 +-
 src/box/iproto_constants.h                       |   1 +
 src/box/lua/execute.c                            |   5 +
 src/box/lua/net_box.c                            |   7 +-
 src/box/sql/select.c                             |   6 +-
 src/box/sql/sqlInt.h                             |   3 +
 src/box/sql/vdbe.h                               |   3 +
 src/box/sql/vdbeInt.h                            |   5 +
 src/box/sql/vdbeapi.c                            |   7 +
 src/box/sql/vdbeaux.c                            |   7 +
 test/box/sql-update-with-nested-select.result    |   5 +-
 test/sql/boolean.result                          | 425 ++++++++++++++---------
 test/sql/check-clear-ephemeral.result            |   5 +-
 test/sql/collation.result                        |  74 ++--
 test/sql/gh-3199-no-mem-leaks.result             | 120 ++++---
 test/sql/gh2141-delete-trigger-drop-table.result |  20 +-
 test/sql/gh2251-multiple-update.result           |  10 +-
 test/sql/iproto.result                           | 105 +++---
 test/sql/misc.result                             |  25 +-
 test/sql/on-conflict.result                      |  20 +-
 test/sql/persistency.result                      | 190 ++++++----
 test/sql/row-count.result                        |  25 +-
 test/sql/sql-debug.result                        |  15 +-
 test/sql/transition.result                       | 190 ++++++----
 test/sql/types.result                            | 105 +++---
 test/sql/update-with-nested-select.result        |   5 +-
 26 files changed, 862 insertions(+), 539 deletions(-)

diff --git a/src/box/execute.c b/src/box/execute.c
index 20bfd0957..98812ae1e 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -267,8 +267,10 @@ error:
 	region_truncate(region, svp);
 	return -1;
 }
+
 static size_t
-metadata_map_sizeof(const char *name, const char *type, const char *coll)
+metadata_map_sizeof(const char *name, const char *type, const char *coll,
+		    int nullable)
 {
 	uint32_t members_count = 2;
 	size_t map_size = 0;
@@ -277,6 +279,11 @@ metadata_map_sizeof(const char *name, const char *type, const char *coll)
 		map_size += mp_sizeof_uint(IPROTO_FIELD_COLL);
 		map_size += mp_sizeof_str(strlen(coll));
 	}
+	if (nullable != -1) {
+		members_count++;
+		map_size += mp_sizeof_uint(IPROTO_FIELD_NULLABLE);
+		map_size += mp_sizeof_bool(nullable);
+	}
 	map_size += mp_sizeof_uint(IPROTO_FIELD_NAME);
 	map_size += mp_sizeof_uint(IPROTO_FIELD_TYPE);
 	map_size += mp_sizeof_str(strlen(name));
@@ -311,6 +318,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);
+		int nullable = sql_column_is_nullable(stmt, i);
 		/*
 		 * Can not fail, since all column names and types
 		 * are preallocated during prepare phase and the
@@ -318,13 +326,13 @@ 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);
+		size = metadata_map_sizeof(name, type, coll, nullable);
 		char *pos = (char *) obuf_alloc(out, size);
 		if (pos == NULL) {
 			diag_set(OutOfMemory, size, "obuf_alloc", "pos");
 			return -1;
 		}
-		uint32_t map_sz = 2 + (coll != NULL);
+		uint32_t map_sz = 2 + (coll != NULL) + (nullable != -1);
 		pos = mp_encode_map(pos, map_sz);
 		pos = mp_encode_uint(pos, IPROTO_FIELD_NAME);
 		pos = mp_encode_str(pos, name, strlen(name));
@@ -334,6 +342,10 @@ sql_get_metadata(struct sql_stmt *stmt, struct obuf *out, int column_count)
 			pos = mp_encode_uint(pos, IPROTO_FIELD_COLL);
 			pos = mp_encode_str(pos, coll, strlen(coll));
 		}
+		if (nullable != -1) {
+			pos = mp_encode_uint(pos, IPROTO_FIELD_NULLABLE);
+			pos = mp_encode_bool(pos, nullable);
+		}
 	}
 	return 0;
 }
diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h
index fa9c029a2..030c25531 100644
--- a/src/box/iproto_constants.h
+++ b/src/box/iproto_constants.h
@@ -132,6 +132,7 @@ enum iproto_metadata_key {
 	IPROTO_FIELD_NAME = 0,
 	IPROTO_FIELD_TYPE = 1,
 	IPROTO_FIELD_COLL = 2,
+	IPROTO_FIELD_NULLABLE = 3
 };
 
 enum iproto_ballot_key {
diff --git a/src/box/lua/execute.c b/src/box/lua/execute.c
index 8a530bfc1..3d7ca710c 100644
--- a/src/box/lua/execute.c
+++ b/src/box/lua/execute.c
@@ -24,6 +24,7 @@ lua_sql_get_metadata(struct sql_stmt *stmt, struct lua_State *L,
 		lua_createtable(L, 0, coll != NULL ? 3 : 2);
 		const char *name = sql_column_name(stmt, i);
 		const char *type = sql_column_datatype(stmt, i);
+		int nullable = sql_column_is_nullable(stmt, i);
 		/*
 		 * Can not fail, since all column names are
 		 * preallocated during prepare phase and the
@@ -39,6 +40,10 @@ lua_sql_get_metadata(struct sql_stmt *stmt, struct lua_State *L,
 			lua_pushstring(L, coll);
 			lua_setfield(L, -2, "collation");
 		}
+		if (nullable != -1) {
+			lua_pushboolean(L, nullable);
+			lua_setfield(L, -2, "is_nullable");
+		}
 		lua_rawseti(L, -2, i + 1);
 	}
 }
diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c
index afbd1e1be..3e93cbc75 100644
--- a/src/box/lua/net_box.c
+++ b/src/box/lua/net_box.c
@@ -651,6 +651,11 @@ decode_metadata_optional(struct lua_State *L, const char **data,
 			const char *coll = mp_decode_str(data, &len);
 			lua_pushlstring(L, coll, len);
 			lua_setfield(L, -2, "collation");
+		} else {
+			assert(key == IPROTO_FIELD_NULLABLE);
+			bool is_nullable = mp_decode_bool(data);
+			lua_pushboolean(L, is_nullable);
+			lua_setfield(L, -2, "is_nullable");
 		}
 	}
 }
@@ -667,7 +672,7 @@ 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 == 3);
+		assert(map_size >= 2 && map_size <= 4);
 		(void) map_size;
 		uint32_t key = mp_decode_uint(data);
 		assert(key == IPROTO_FIELD_NAME);
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 66e8c1274..b772bcead 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1810,6 +1810,7 @@ generate_column_metadata(struct Parse *pParse, struct SrcList *pTabList,
 								coll_id->name_len);
 			}
 		}
+		vdbe_set_metadata_col_nullability(v, i, -1);
 		if (pEList->a[i].zName) {
 			char *zName = pEList->a[i].zName;
 			vdbe_set_metadata_col_name(v, i, zName);
@@ -1836,7 +1837,10 @@ generate_column_metadata(struct Parse *pParse, struct SrcList *pTabList,
 			} else {
 				vdbe_set_metadata_col_name(v, i, zCol);
 			}
-
+			bool is_nullable = space_def->fields[iCol].is_nullable;
+			if (p->op == TK_COLUMN)
+				vdbe_set_metadata_col_nullability(v, i,
+								  is_nullable);
 		} else {
 			const char *z = pEList->a[i].zSpan;
 			if (z == NULL)
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 4c2e3ed73..89920b3d1 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -579,6 +579,9 @@ sql_column_datatype(sql_stmt *, int N);
 const char *
 sql_column_coll(sql_stmt *stmt, int n);
 
+int
+sql_column_is_nullable(sql_stmt *stmt, int n);
+
 int
 sql_initialize(void);
 
diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
index 5f042d7af..0f315b660 100644
--- a/src/box/sql/vdbe.h
+++ b/src/box/sql/vdbe.h
@@ -257,6 +257,9 @@ int
 vdbe_set_metadata_col_collation(struct Vdbe *p, int idx, const char *coll,
 				size_t coll_len);
 
+void
+vdbe_set_metadata_col_nullability(struct Vdbe *p, int idx, int nullable);
+
 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 fba86d664..0e54e42a5 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -350,6 +350,11 @@ struct sql_column_metadata {
 	const char *name;
 	const char *type;
 	const char *collation;
+	/**
+	 * -1 is for any member of result set except for pure
+	 * columns: all other expressions are nullable by default.
+	 */
+	int8_t nullable : 2;
 };
 
 /*
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index 6895d0ad5..ea8c7c438 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -753,6 +753,13 @@ sql_column_coll(sql_stmt *stmt, int n)
 	return p->metadata[n].collation;
 }
 
+int
+sql_column_is_nullable(sql_stmt *stmt, int n)
+{
+	struct Vdbe *p = (struct Vdbe *) stmt;
+	assert(n < sql_column_count(stmt) && n >= 0);
+	return p->metadata[n].nullable;
+}
 
 /******************************* sql_bind_  **************************
  *
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 1707df7f0..6c3523ba4 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -1904,6 +1904,13 @@ vdbe_set_metadata_col_collation(struct Vdbe *p, int idx, const char *coll,
 	return 0;
 }
 
+void
+vdbe_set_metadata_col_nullability(struct Vdbe *p, int idx, int nullable)
+{
+	assert(idx < p->nResColumn);
+	p->metadata[idx].nullable = nullable;
+}
+
 /*
  * 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/box/sql-update-with-nested-select.result b/test/box/sql-update-with-nested-select.result
index 4ff090f3a..ea627f769 100644
--- a/test/box/sql-update-with-nested-select.result
+++ b/test/box/sql-update-with-nested-select.result
@@ -27,8 +27,9 @@ box.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
 box.execute("SELECT e FROM t1");
 ---
 - metadata:
-  - name: E
-    type: integer
+  - type: integer
+    name: E
+    is_nullable: true
   rows:
   - [7]
   - [8]
diff --git a/test/sql/boolean.result b/test/sql/boolean.result
index 7769d0cb3..339e7d9d0 100644
--- a/test/sql/boolean.result
+++ b/test/sql/boolean.result
@@ -48,16 +48,18 @@ test_run:cmd("setopt delimiter ''");
 SELECT a FROM t WHERE a;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   rows:
  |   - [true]
  | ...
 SELECT a FROM t WHERE a != true;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   rows:
  |   - [false]
  | ...
@@ -123,8 +125,9 @@ INSERT INTO ts SELECT * FROM t0;
 SELECT s FROM ts WHERE s = true;
  | ---
  | - metadata:
- |   - name: S
- |     type: scalar
+ |   - type: scalar
+ |     name: S
+ |     is_nullable: true
  |   rows:
  |   - [true]
  | ...
@@ -148,8 +151,9 @@ SELECT s FROM ts WHERE s < true;
 SELECT s FROM ts WHERE s IN (true, 1, 'abcd');
  | ---
  | - metadata:
- |   - name: S
- |     type: scalar
+ |   - type: scalar
+ |     name: S
+ |     is_nullable: true
  |   rows:
  |   - [true]
  | ...
@@ -202,14 +206,18 @@ SELECT * FROM t LIMIT 1 OFFSET false;
 EXPLAIN QUERY PLAN SELECT a FROM t0 WHERE a = true;
  | ---
  | - metadata:
- |   - name: selectid
- |     type: integer
- |   - name: order
- |     type: integer
- |   - name: from
- |     type: integer
- |   - name: detail
- |     type: text
+ |   - type: integer
+ |     name: selectid
+ |     is_nullable: false
+ |   - type: integer
+ |     name: order
+ |     is_nullable: false
+ |   - type: integer
+ |     name: from
+ |     is_nullable: false
+ |   - type: text
+ |     name: detail
+ |     is_nullable: false
  |   rows:
  |   - [0, 0, 0, 'SEARCH TABLE T0 USING COVERING INDEX I0 (A=?) (~10 rows)']
  | ...
@@ -748,10 +756,12 @@ WITH RECURSIVE cnt(x, y) AS \
 SELECT x, y FROM cnt;
  | ---
  | - metadata:
- |   - name: X
- |     type: integer
- |   - name: Y
- |     type: boolean
+ |   - type: integer
+ |     name: X
+ |     is_nullable: true
+ |   - type: boolean
+ |     name: Y
+ |     is_nullable: true
  |   rows:
  |   - [1, false]
  |   - [2, true]
@@ -849,8 +859,9 @@ END AS a0 \
 FROM t4;
  | ---
  | - metadata:
- |   - name: I
- |     type: integer
+ |   - type: integer
+ |     name: I
+ |     is_nullable: false
  |   - name: A0
  |     type: boolean
  |   rows:
@@ -918,8 +929,9 @@ SELECT NOT false;
 SELECT a, NOT a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: NOT a
  |     type: boolean
  |   rows:
@@ -995,8 +1007,9 @@ SELECT false OR false;
 SELECT a, true AND a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: true AND a
  |     type: boolean
  |   rows:
@@ -1006,8 +1019,9 @@ SELECT a, true AND a FROM t;
 SELECT a, false AND a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: false AND a
  |     type: boolean
  |   rows:
@@ -1017,8 +1031,9 @@ SELECT a, false AND a FROM t;
 SELECT a, true OR a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: true OR a
  |     type: boolean
  |   rows:
@@ -1028,8 +1043,9 @@ SELECT a, true OR a FROM t;
 SELECT a, false OR a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: false OR a
  |     type: boolean
  |   rows:
@@ -1039,8 +1055,9 @@ SELECT a, false OR a FROM t;
 SELECT a, a AND true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a AND true
  |     type: boolean
  |   rows:
@@ -1050,8 +1067,9 @@ SELECT a, a AND true FROM t;
 SELECT a, a AND false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a AND false
  |     type: boolean
  |   rows:
@@ -1061,8 +1079,9 @@ SELECT a, a AND false FROM t;
 SELECT a, a OR true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a OR true
  |     type: boolean
  |   rows:
@@ -1072,8 +1091,9 @@ SELECT a, a OR true FROM t;
 SELECT a, a OR false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a OR false
  |     type: boolean
  |   rows:
@@ -1084,10 +1104,12 @@ SELECT a, a OR false FROM t;
 SELECT a, a1, a AND a1 FROM t, t6;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
- |   - name: A1
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
+ |   - type: boolean
+ |     name: A1
+ |     is_nullable: false
  |   - name: a AND a1
  |     type: boolean
  |   rows:
@@ -1099,10 +1121,12 @@ SELECT a, a1, a AND a1 FROM t, t6;
 SELECT a, a1, a OR a1 FROM t, t6;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
- |   - name: A1
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
+ |   - type: boolean
+ |     name: A1
+ |     is_nullable: false
  |   - name: a OR a1
  |     type: boolean
  |   rows:
@@ -1682,8 +1706,9 @@ SELECT false < false;
 SELECT a, true > a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: true > a
  |     type: boolean
  |   rows:
@@ -1693,8 +1718,9 @@ SELECT a, true > a FROM t;
 SELECT a, false > a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: false > a
  |     type: boolean
  |   rows:
@@ -1704,8 +1730,9 @@ SELECT a, false > a FROM t;
 SELECT a, true < a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: true < a
  |     type: boolean
  |   rows:
@@ -1715,8 +1742,9 @@ SELECT a, true < a FROM t;
 SELECT a, false < a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: false < a
  |     type: boolean
  |   rows:
@@ -1726,8 +1754,9 @@ SELECT a, false < a FROM t;
 SELECT a, a > true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a > true
  |     type: boolean
  |   rows:
@@ -1737,8 +1766,9 @@ SELECT a, a > true FROM t;
 SELECT a, a > false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a > false
  |     type: boolean
  |   rows:
@@ -1748,8 +1778,9 @@ SELECT a, a > false FROM t;
 SELECT a, a < true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a < true
  |     type: boolean
  |   rows:
@@ -1759,8 +1790,9 @@ SELECT a, a < true FROM t;
 SELECT a, a < false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a < false
  |     type: boolean
  |   rows:
@@ -1771,10 +1803,12 @@ SELECT a, a < false FROM t;
 SELECT a, a1, a > a1 FROM t, t6;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
- |   - name: A1
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
+ |   - type: boolean
+ |     name: A1
+ |     is_nullable: false
  |   - name: a > a1
  |     type: boolean
  |   rows:
@@ -1786,10 +1820,12 @@ SELECT a, a1, a > a1 FROM t, t6;
 SELECT a, a1, a < a1 FROM t, t6;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
- |   - name: A1
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
+ |   - type: boolean
+ |     name: A1
+ |     is_nullable: false
  |   - name: a < a1
  |     type: boolean
  |   rows:
@@ -1867,8 +1903,9 @@ SELECT false <= false;
 SELECT a, true >= a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: true >= a
  |     type: any
  |   rows:
@@ -1878,8 +1915,9 @@ SELECT a, true >= a FROM t;
 SELECT a, false >= a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: false >= a
  |     type: any
  |   rows:
@@ -1889,8 +1927,9 @@ SELECT a, false >= a FROM t;
 SELECT a, true <= a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: true <= a
  |     type: boolean
  |   rows:
@@ -1900,8 +1939,9 @@ SELECT a, true <= a FROM t;
 SELECT a, false <= a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: false <= a
  |     type: boolean
  |   rows:
@@ -1911,8 +1951,9 @@ SELECT a, false <= a FROM t;
 SELECT a, a >= true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a >= true
  |     type: any
  |   rows:
@@ -1922,8 +1963,9 @@ SELECT a, a >= true FROM t;
 SELECT a, a >= false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a >= false
  |     type: any
  |   rows:
@@ -1933,8 +1975,9 @@ SELECT a, a >= false FROM t;
 SELECT a, a <= true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a <= true
  |     type: boolean
  |   rows:
@@ -1944,8 +1987,9 @@ SELECT a, a <= true FROM t;
 SELECT a, a <= false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a <= false
  |     type: boolean
  |   rows:
@@ -1956,10 +2000,12 @@ SELECT a, a <= false FROM t;
 SELECT a, a1, a >= a1 FROM t, t6;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
- |   - name: A1
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
+ |   - type: boolean
+ |     name: A1
+ |     is_nullable: false
  |   - name: a >= a1
  |     type: any
  |   rows:
@@ -1971,10 +2017,12 @@ SELECT a, a1, a >= a1 FROM t, t6;
 SELECT a, a1, a <= a1 FROM t, t6;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
- |   - name: A1
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
+ |   - type: boolean
+ |     name: A1
+ |     is_nullable: false
  |   - name: a <= a1
  |     type: boolean
  |   rows:
@@ -2052,8 +2100,9 @@ SELECT false != false;
 SELECT a, true == a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: true == a
  |     type: boolean
  |   rows:
@@ -2063,8 +2112,9 @@ SELECT a, true == a FROM t;
 SELECT a, false == a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: false == a
  |     type: boolean
  |   rows:
@@ -2074,8 +2124,9 @@ SELECT a, false == a FROM t;
 SELECT a, true != a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: true != a
  |     type: boolean
  |   rows:
@@ -2085,8 +2136,9 @@ SELECT a, true != a FROM t;
 SELECT a, false != a FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: false != a
  |     type: boolean
  |   rows:
@@ -2096,8 +2148,9 @@ SELECT a, false != a FROM t;
 SELECT a, a == true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a == true
  |     type: boolean
  |   rows:
@@ -2107,8 +2160,9 @@ SELECT a, a == true FROM t;
 SELECT a, a == false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a == false
  |     type: boolean
  |   rows:
@@ -2118,8 +2172,9 @@ SELECT a, a == false FROM t;
 SELECT a, a != true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a != true
  |     type: boolean
  |   rows:
@@ -2129,8 +2184,9 @@ SELECT a, a != true FROM t;
 SELECT a, a != false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a != false
  |     type: boolean
  |   rows:
@@ -2141,10 +2197,12 @@ SELECT a, a != false FROM t;
 SELECT a, a1, a == a1 FROM t, t6;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
- |   - name: A1
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
+ |   - type: boolean
+ |     name: A1
+ |     is_nullable: false
  |   - name: a == a1
  |     type: boolean
  |   rows:
@@ -2156,10 +2214,12 @@ SELECT a, a1, a == a1 FROM t, t6;
 SELECT a, a1, a != a1 FROM t, t6;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
- |   - name: A1
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
+ |   - type: boolean
+ |     name: A1
+ |     is_nullable: false
  |   - name: a != a1
  |     type: boolean
  |   rows:
@@ -2269,8 +2329,9 @@ SELECT false IN (1, 1.2, 'true', false);
 SELECT a, a IN (true) FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a IN (true)
  |     type: boolean
  |   rows:
@@ -2280,8 +2341,9 @@ SELECT a, a IN (true) FROM t;
 SELECT a, a IN (false) FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a IN (false)
  |     type: boolean
  |   rows:
@@ -2291,8 +2353,9 @@ SELECT a, a IN (false) FROM t;
 SELECT a, a IN (true, false) FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a IN (true, false)
  |     type: boolean
  |   rows:
@@ -2302,8 +2365,9 @@ SELECT a, a IN (true, false) FROM t;
 SELECT a, a IN (SELECT a1 FROM t6 LIMIT 1) FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a IN (SELECT a1 FROM t6 LIMIT 1)
  |     type: boolean
  |   rows:
@@ -2313,8 +2377,9 @@ SELECT a, a IN (SELECT a1 FROM t6 LIMIT 1) FROM t;
 SELECT a, a IN (SELECT a1 FROM t6) FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a IN (SELECT a1 FROM t6)
  |     type: boolean
  |   rows:
@@ -2324,8 +2389,9 @@ SELECT a, a IN (SELECT a1 FROM t6) FROM t;
 SELECT a, a IN (1, 1.2, 'true', false) FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a IN (1, 1.2, 'true', false)
  |     type: boolean
  |   rows:
@@ -2401,8 +2467,9 @@ SELECT false BETWEEN false AND true;
 SELECT a, a BETWEEN true AND true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a BETWEEN true AND true
  |     type: boolean
  |   rows:
@@ -2412,8 +2479,9 @@ SELECT a, a BETWEEN true AND true FROM t;
 SELECT a, a BETWEEN false AND false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a BETWEEN false AND false
  |     type: boolean
  |   rows:
@@ -2423,8 +2491,9 @@ SELECT a, a BETWEEN false AND false FROM t;
 SELECT a, a BETWEEN true AND false FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a BETWEEN true AND false
  |     type: boolean
  |   rows:
@@ -2434,8 +2503,9 @@ SELECT a, a BETWEEN true AND false FROM t;
 SELECT a, a BETWEEN false AND true FROM t;
  | ---
  | - metadata:
- |   - name: A
- |     type: boolean
+ |   - type: boolean
+ |     name: A
+ |     is_nullable: false
  |   - name: a BETWEEN false AND true
  |     type: boolean
  |   rows:
@@ -2549,8 +2619,9 @@ SELECT b, true AND b FROM t7;
 SELECT b, false AND b FROM t7;
  | ---
  | - metadata:
- |   - name: B
- |     type: integer
+ |   - type: integer
+ |     name: B
+ |     is_nullable: false
  |   - name: false AND b
  |     type: boolean
  |   rows:
@@ -2574,8 +2645,9 @@ SELECT b, b AND true FROM t7;
 SELECT b, b AND false FROM t7;
  | ---
  | - metadata:
- |   - name: B
- |     type: integer
+ |   - type: integer
+ |     name: B
+ |     is_nullable: false
  |   - name: b AND false
  |     type: boolean
  |   rows:
@@ -3882,8 +3954,9 @@ SELECT false IN (SELECT b FROM t7);
 SELECT a1, a1 IN (0, 1, 2, 3) FROM t6
  | ---
  | - metadata:
- |   - name: A1
- |     type: boolean
+ |   - type: boolean
+ |     name: A1
+ |     is_nullable: false
  |   - name: a1 IN (0, 1, 2, 3)
  |     type: boolean
  |   rows:
@@ -4018,8 +4091,9 @@ SELECT c, true AND c FROM t8;
 SELECT c, false AND c FROM t8;
  | ---
  | - metadata:
- |   - name: C
- |     type: number
+ |   - type: number
+ |     name: C
+ |     is_nullable: false
  |   - name: false AND c
  |     type: boolean
  |   rows:
@@ -4043,8 +4117,9 @@ SELECT c, c AND true FROM t8;
 SELECT c, c AND false FROM t8;
  | ---
  | - metadata:
- |   - name: C
- |     type: number
+ |   - type: number
+ |     name: C
+ |     is_nullable: false
  |   - name: c AND false
  |     type: boolean
  |   rows:
@@ -5178,8 +5253,9 @@ SELECT d, true AND d FROM t9;
 SELECT d, false AND d FROM t9;
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: false AND d
  |     type: boolean
  |   rows:
@@ -5203,8 +5279,9 @@ SELECT d, d AND true FROM t9;
 SELECT d, d AND false FROM t9;
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: d AND false
  |     type: boolean
  |   rows:
@@ -5553,8 +5630,9 @@ SELECT d, true AND d FROM t9 WHERE d = 'TRUE';
 SELECT d, false AND d FROM t9 WHERE d = 'TRUE';
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: false AND d
  |     type: boolean
  |   rows:
@@ -5578,8 +5656,9 @@ SELECT d, d AND true FROM t9 WHERE d = 'TRUE';
 SELECT d, d AND false FROM t9 WHERE d = 'TRUE';
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: d AND false
  |     type: boolean
  |   rows:
@@ -5733,8 +5812,9 @@ SELECT d, true AND d FROM t9 WHERE d = 'true';
 SELECT d, false AND d FROM t9 WHERE d = 'true';
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: false AND d
  |     type: boolean
  |   rows:
@@ -5758,8 +5838,9 @@ SELECT d, d AND true FROM t9 WHERE d = 'true';
 SELECT d, d AND false FROM t9 WHERE d = 'true';
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: d AND false
  |     type: boolean
  |   rows:
@@ -5913,8 +5994,9 @@ SELECT d, true AND d FROM t9 WHERE d = 'FALSE';
 SELECT d, false AND d FROM t9 WHERE d = 'FALSE';
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: false AND d
  |     type: boolean
  |   rows:
@@ -5938,8 +6020,9 @@ SELECT d, d AND true FROM t9 WHERE d = 'FALSE';
 SELECT d, d AND false FROM t9 WHERE d = 'FALSE';
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: d AND false
  |     type: boolean
  |   rows:
@@ -6093,8 +6176,9 @@ SELECT d, true AND d FROM t9 WHERE d = 'false';
 SELECT d, false AND d FROM t9 WHERE d = 'false';
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: false AND d
  |     type: boolean
  |   rows:
@@ -6118,8 +6202,9 @@ SELECT d, d AND true FROM t9 WHERE d = 'false';
 SELECT d, d AND false FROM t9 WHERE d = 'false';
  | ---
  | - metadata:
- |   - name: D
- |     type: string
+ |   - type: string
+ |     name: D
+ |     is_nullable: false
  |   - name: d AND false
  |     type: boolean
  |   rows:
diff --git a/test/sql/check-clear-ephemeral.result b/test/sql/check-clear-ephemeral.result
index 7d0be5ffb..dd9660220 100644
--- a/test/sql/check-clear-ephemeral.result
+++ b/test/sql/check-clear-ephemeral.result
@@ -25,8 +25,9 @@ box.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt W
 box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows:
   - [840]
   - [880]
diff --git a/test/sql/collation.result b/test/sql/collation.result
index 137a38bb4..750e9c509 100644
--- a/test/sql/collation.result
+++ b/test/sql/collation.result
@@ -67,8 +67,9 @@ box.execute([[INSERT INTO tu VALUES ('Latin Small Letter Dotless I U+0131','ı')
 box.execute([[SELECT descriptor, upper(letter) AS upper,lower(letter) AS lower FROM tu;]])
 ---
 - metadata:
-  - name: DESCRIPTOR
-    type: string
+  - type: string
+    name: DESCRIPTOR
+    is_nullable: false
   - name: UPPER
     type: string
   - name: LOWER
@@ -83,8 +84,9 @@ box.execute([[SELECT descriptor, upper(letter) AS upper,lower(letter) AS lower F
 box.execute([[SELECT descriptor, upper(letter COLLATE "TURKISH") AS upper,lower(letter COLLATE "TURKISH") AS lower FROM tu;]])
 ---
 - metadata:
-  - name: DESCRIPTOR
-    type: string
+  - type: string
+    name: DESCRIPTOR
+    is_nullable: false
   - type: string
     name: UPPER
     collation: TURKISH
@@ -112,12 +114,14 @@ box.execute([[INSERT INTO tu VALUES ('German Small Letter Sharp S U+00DF','ß');
 box.execute([[SELECT descriptor, upper(letter), letter FROM tu where UPPER(letter) = 'SS';]])
 ---
 - metadata:
-  - name: DESCRIPTOR
-    type: string
+  - type: string
+    name: DESCRIPTOR
+    is_nullable: false
   - name: upper(letter)
     type: string
-  - name: LETTER
-    type: string
+  - type: string
+    name: LETTER
+    is_nullable: true
   rows:
   - ['German Small Letter Sharp S U+00DF', 'SS', 'ß']
 ...
@@ -125,13 +129,15 @@ box.execute([[SELECT descriptor, upper(letter), letter FROM tu where UPPER(lette
 box.execute([[SELECT descriptor, upper(letter COLLATE "GERMAN"), letter FROM tu where UPPER(letter COLLATE "GERMAN") = 'SS';]])
 ---
 - metadata:
-  - name: DESCRIPTOR
-    type: string
+  - type: string
+    name: DESCRIPTOR
+    is_nullable: false
   - type: string
     name: upper(letter COLLATE "GERMAN")
     collation: GERMAN
-  - name: LETTER
-    type: string
+  - type: string
+    name: LETTER
+    is_nullable: true
   rows:
   - ['German Small Letter Sharp S U+00DF', 'SS', 'ß']
 ...
@@ -325,6 +331,7 @@ box.execute("SELECT b FROM t UNION SELECT a FROM t;")
 ---
 - metadata:
   - type: string
+    is_nullable: true
     name: B
     collation: binary
   rows: []
@@ -332,8 +339,9 @@ box.execute("SELECT b FROM t UNION SELECT a FROM t;")
 box.execute("SELECT a FROM t UNION SELECT c FROM t;")
 ---
 - metadata:
-  - name: A
-    type: string
+  - type: string
+    name: A
+    is_nullable: true
   rows: []
 ...
 box.execute("SELECT c COLLATE \"binary\" FROM t UNION SELECT a FROM t;")
@@ -407,6 +415,7 @@ box.execute("SELECT s1 FROM t0;")
 ---
 - metadata:
   - type: string
+    is_nullable: true
     name: S1
     collation: unicode_ci
   rows:
@@ -469,8 +478,9 @@ box.execute("SELECT * FROM t1;")
 box.execute("SELECT s1 FROM t0;")
 ---
 - metadata:
-  - name: S1
-    type: string
+  - type: string
+    name: S1
+    is_nullable: true
   rows:
   - ['a']
 ...
@@ -502,8 +512,9 @@ box.execute("INSERT INTO t4a VALUES('ghi','ghi',3);")
 box.execute("SELECT c FROM t4a WHERE (a||'') = b;")
 ---
 - metadata:
-  - name: C
-    type: integer
+  - type: integer
+    name: C
+    is_nullable: false
   rows:
   - [1]
   - [3]
@@ -514,8 +525,9 @@ box.execute("SELECT c FROM t4a WHERE (a||'') = b;")
 box.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b;")
 ---
 - metadata:
-  - name: C
-    type: integer
+  - type: integer
+    name: C
+    is_nullable: false
   rows:
   - [3]
 ...
@@ -538,16 +550,18 @@ box.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b COLLATE \"un
 box.execute("SELECT c FROM t4a WHERE (a||'')=(b||'');")
 ---
 - metadata:
-  - name: C
-    type: integer
+  - type: integer
+    name: C
+    is_nullable: false
   rows:
   - [3]
 ...
 box.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
 ---
 - metadata:
-  - name: C
-    type: integer
+  - type: integer
+    name: C
+    is_nullable: false
   rows:
   - [3]
 ...
@@ -568,8 +582,9 @@ box.execute("INSERT INTO t4b VALUES('ghi','ghi',3);")
 box.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
 ---
 - metadata:
-  - name: C
-    type: integer
+  - type: integer
+    name: C
+    is_nullable: false
   rows:
   - [3]
 ...
@@ -578,8 +593,9 @@ box.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
 box.execute("SELECT c FROM t4a WHERE (a||b COLLATE \"binary\")=(b||a);")
 ---
 - metadata:
-  - name: C
-    type: integer
+  - type: integer
+    name: C
+    is_nullable: false
   rows:
   - [3]
 ...
@@ -603,6 +619,7 @@ box.execute("SELECT a FROM t4b ORDER BY a COLLATE \"unicode_ci\" || ''")
 ---
 - metadata:
   - type: string
+    is_nullable: true
     name: A
     collation: unicode_ci
   rows:
@@ -615,6 +632,7 @@ box.execute("SELECT a FROM t4b ORDER BY a || b")
 ---
 - metadata:
   - type: string
+    is_nullable: true
     name: A
     collation: unicode_ci
   rows:
diff --git a/test/sql/gh-3199-no-mem-leaks.result b/test/sql/gh-3199-no-mem-leaks.result
index e7ba1d29c..6dc5ad212 100644
--- a/test/sql/gh-3199-no-mem-leaks.result
+++ b/test/sql/gh-3199-no-mem-leaks.result
@@ -26,10 +26,12 @@ box.execute('INSERT INTO test VALUES (1, 1, 1), (2, 2, 2)')
 box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 ---
 - metadata:
-  - name: X
-    type: integer
-  - name: Y
-    type: integer
+  - type: integer
+    name: X
+    is_nullable: true
+  - type: integer
+    name: Y
+    is_nullable: true
   - name: x + y
     type: integer
   rows:
@@ -43,10 +45,12 @@ fiber.info()[fiber.self().id()].memory.used
 box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 ---
 - metadata:
-  - name: X
-    type: integer
-  - name: Y
-    type: integer
+  - type: integer
+    name: X
+    is_nullable: true
+  - type: integer
+    name: Y
+    is_nullable: true
   - name: x + y
     type: integer
   rows:
@@ -56,10 +60,12 @@ box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 ---
 - metadata:
-  - name: X
-    type: integer
-  - name: Y
-    type: integer
+  - type: integer
+    name: X
+    is_nullable: true
+  - type: integer
+    name: Y
+    is_nullable: true
   - name: x + y
     type: integer
   rows:
@@ -69,10 +75,12 @@ box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 ---
 - metadata:
-  - name: X
-    type: integer
-  - name: Y
-    type: integer
+  - type: integer
+    name: X
+    is_nullable: true
+  - type: integer
+    name: Y
+    is_nullable: true
   - name: x + y
     type: integer
   rows:
@@ -82,10 +90,12 @@ box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 ---
 - metadata:
-  - name: X
-    type: integer
-  - name: Y
-    type: integer
+  - type: integer
+    name: X
+    is_nullable: true
+  - type: integer
+    name: Y
+    is_nullable: true
   - name: x + y
     type: integer
   rows:
@@ -111,12 +121,14 @@ box.execute('INSERT INTO test2 VALUES (3, \'test\', 3), (4, \'xx\', 4)')
 box.execute('SELECT a, id + 2, b FROM test2 WHERE b < id * 2 ORDER BY a ')
 ---
 - metadata:
-  - name: A
-    type: string
+  - type: string
+    name: A
+    is_nullable: true
   - name: id + 2
     type: integer
-  - name: B
-    type: integer
+  - type: integer
+    name: B
+    is_nullable: true
   rows:
   - ['abc', 3, 1]
   - ['hello', 4, 2]
@@ -130,12 +142,14 @@ fiber.info()[fiber.self().id()].memory.used
 box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
 ---
 - metadata:
-  - name: A
-    type: string
+  - type: string
+    name: A
+    is_nullable: true
   - name: id + 2 * b
     type: integer
-  - name: A
-    type: string
+  - type: string
+    name: A
+    is_nullable: true
   rows:
   - ['abc', 3, 'abc']
   - ['hello', 6, 'hello']
@@ -145,12 +159,14 @@ box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
 box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
 ---
 - metadata:
-  - name: A
-    type: string
+  - type: string
+    name: A
+    is_nullable: true
   - name: id + 2 * b
     type: integer
-  - name: A
-    type: string
+  - type: string
+    name: A
+    is_nullable: true
   rows:
   - ['abc', 3, 'abc']
   - ['hello', 6, 'hello']
@@ -160,12 +176,14 @@ box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
 box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
 ---
 - metadata:
-  - name: A
-    type: string
+  - type: string
+    name: A
+    is_nullable: true
   - name: id + 2 * b
     type: integer
-  - name: A
-    type: string
+  - type: string
+    name: A
+    is_nullable: true
   rows:
   - ['abc', 3, 'abc']
   - ['hello', 6, 'hello']
@@ -179,12 +197,14 @@ fiber.info()[fiber.self().id()].memory.used
 box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
 ---
 - metadata:
-  - name: X
-    type: integer
+  - type: integer
+    name: X
+    is_nullable: true
   - name: y + 3 * b
     type: integer
-  - name: B
-    type: integer
+  - type: integer
+    name: B
+    is_nullable: true
   rows:
   - [1, 4, 1]
   - [2, 8, 2]
@@ -192,12 +212,14 @@ box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
 box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
 ---
 - metadata:
-  - name: X
-    type: integer
+  - type: integer
+    name: X
+    is_nullable: true
   - name: y + 3 * b
     type: integer
-  - name: B
-    type: integer
+  - type: integer
+    name: B
+    is_nullable: true
   rows:
   - [1, 4, 1]
   - [2, 8, 2]
@@ -205,12 +227,14 @@ box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
 box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
 ---
 - metadata:
-  - name: X
-    type: integer
+  - type: integer
+    name: X
+    is_nullable: true
   - name: y + 3 * b
     type: integer
-  - name: B
-    type: integer
+  - type: integer
+    name: B
+    is_nullable: true
   rows:
   - [1, 4, 1]
   - [2, 8, 2]
diff --git a/test/sql/gh2141-delete-trigger-drop-table.result b/test/sql/gh2141-delete-trigger-drop-table.result
index 1d373f57e..735b932a5 100644
--- a/test/sql/gh2141-delete-trigger-drop-table.result
+++ b/test/sql/gh2141-delete-trigger-drop-table.result
@@ -41,10 +41,12 @@ box.execute("CREATE TRIGGER tt_ad AFTER DELETE ON t FOR EACH ROW BEGIN SELECT 1;
 box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 ---
 - metadata:
-  - name: name
-    type: string
-  - name: opts
-    type: map
+  - type: string
+    name: name
+    is_nullable: false
+  - type: map
+    name: opts
+    is_nullable: false
   rows:
   - ['TT_AD', {'sql': 'CREATE TRIGGER tt_ad AFTER DELETE ON t FOR EACH ROW BEGIN SELECT
         1; END'}]
@@ -68,9 +70,11 @@ box.execute("DROP TABLE t")
 box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 ---
 - metadata:
-  - name: name
-    type: string
-  - name: opts
-    type: map
+  - type: string
+    name: name
+    is_nullable: false
+  - type: map
+    name: opts
+    is_nullable: false
   rows: []
 ...
diff --git a/test/sql/gh2251-multiple-update.result b/test/sql/gh2251-multiple-update.result
index 42ebf7f55..a56c4b8b7 100644
--- a/test/sql/gh2251-multiple-update.result
+++ b/test/sql/gh2251-multiple-update.result
@@ -29,8 +29,9 @@ box.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);")
 box.execute("SELECT e FROM t1")
 ---
 - metadata:
-  - name: E
-    type: integer
+  - type: integer
+    name: E
+    is_nullable: true
   rows:
   - [7]
   - [8]
@@ -54,8 +55,9 @@ box.execute("UPDATE t2 SET e=e+1 WHERE b IN (SELECT b FROM t2);")
 box.execute("SELECT e FROM t2")
 ---
 - metadata:
-  - name: E
-    type: integer
+  - type: integer
+    name: E
+    is_nullable: true
   rows:
   - [6]
   - [7]
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 67acd0ac1..05c59318c 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -682,18 +682,24 @@ res = cn:execute("PRAGMA table_info(t1)")
 ...
 res.metadata
 ---
-- - name: cid
-    type: integer
-  - name: name
-    type: text
-  - name: type
-    type: text
-  - name: notnull
-    type: integer
-  - name: dflt_value
-    type: text
-  - name: pk
-    type: integer
+- - type: integer
+    name: cid
+    is_nullable: false
+  - type: text
+    name: name
+    is_nullable: false
+  - type: text
+    name: type
+    is_nullable: false
+  - type: integer
+    name: notnull
+    is_nullable: false
+  - type: text
+    name: dflt_value
+    is_nullable: false
+  - type: integer
+    name: pk
+    is_nullable: false
 ...
 -- EXPLAIN
 res = cn:execute("EXPLAIN SELECT 1")
@@ -701,36 +707,48 @@ res = cn:execute("EXPLAIN SELECT 1")
 ...
 res.metadata
 ---
-- - name: addr
-    type: integer
-  - name: opcode
-    type: text
-  - name: p1
-    type: integer
-  - name: p2
-    type: integer
-  - name: p3
-    type: integer
-  - name: p4
-    type: text
-  - name: p5
-    type: text
-  - name: comment
-    type: text
+- - type: integer
+    name: addr
+    is_nullable: false
+  - type: text
+    name: opcode
+    is_nullable: false
+  - type: integer
+    name: p1
+    is_nullable: false
+  - type: integer
+    name: p2
+    is_nullable: false
+  - type: integer
+    name: p3
+    is_nullable: false
+  - type: text
+    name: p4
+    is_nullable: false
+  - type: text
+    name: p5
+    is_nullable: false
+  - type: text
+    name: comment
+    is_nullable: false
 ...
 res = cn:execute("EXPLAIN QUERY PLAN SELECT COUNT(*) FROM t1")
 ---
 ...
 res.metadata
 ---
-- - name: selectid
-    type: integer
-  - name: order
-    type: integer
-  - name: from
-    type: integer
-  - name: detail
-    type: text
+- - type: integer
+    name: selectid
+    is_nullable: false
+  - type: integer
+    name: order
+    is_nullable: false
+  - type: integer
+    name: from
+    is_nullable: false
+  - type: text
+    name: detail
+    is_nullable: false
 ...
 -- When pragma count_changes is on, statements INSERT, REPLACE and
 -- UPDATE returns number of changed columns. Make sure that this
@@ -742,24 +760,27 @@ cn:execute("PRAGMA count_changes = 1;")
 cn:execute("INSERT INTO t1 VALUES (1), (2), (3);")
 ---
 - metadata:
-  - name: rows inserted
-    type: integer
+  - type: integer
+    name: rows inserted
+    is_nullable: false
   rows:
   - [3]
 ...
 cn:execute("REPLACE INTO t1 VALUES (2), (3), (4), (5);")
 ---
 - metadata:
-  - name: rows replaced
-    type: integer
+  - type: integer
+    name: rows replaced
+    is_nullable: false
   rows:
   - [4]
 ...
 cn:execute("UPDATE t1 SET id = id + 100 WHERE id > 10;")
 ---
 - metadata:
-  - name: rows updated
-    type: integer
+  - type: integer
+    name: rows updated
+    is_nullable: false
   rows:
   - [0]
 ...
diff --git a/test/sql/misc.result b/test/sql/misc.result
index a157ddbc1..820cf5119 100644
--- a/test/sql/misc.result
+++ b/test/sql/misc.result
@@ -174,10 +174,12 @@ s:insert(t)
 box.execute('SELECT field70, field64 FROM test')
 ---
 - metadata:
-  - name: FIELD70
-    type: unsigned
-  - name: FIELD64
-    type: unsigned
+  - type: unsigned
+    name: FIELD70
+    is_nullable: false
+  - type: unsigned
+    name: FIELD64
+    is_nullable: false
   rows:
   - [70, 64]
 ...
@@ -188,12 +190,15 @@ pk:alter({parts = {66}})
 box.execute('SELECT field66, field68, field70 FROM test')
 ---
 - metadata:
-  - name: FIELD66
-    type: unsigned
-  - name: FIELD68
-    type: unsigned
-  - name: FIELD70
-    type: unsigned
+  - type: unsigned
+    name: FIELD66
+    is_nullable: false
+  - type: unsigned
+    name: FIELD68
+    is_nullable: false
+  - type: unsigned
+    name: FIELD70
+    is_nullable: false
   rows:
   - [66, 68, 70]
 ...
diff --git a/test/sql/on-conflict.result b/test/sql/on-conflict.result
index 6851e217e..909b68d8f 100644
--- a/test/sql/on-conflict.result
+++ b/test/sql/on-conflict.result
@@ -130,10 +130,12 @@ box.execute("UPDATE OR IGNORE tj SET s1 = s1 + 1;")
 box.execute("SELECT s1, s2 FROM tj;")
 ---
 - metadata:
-  - name: S1
-    type: integer
-  - name: S2
-    type: integer
+  - type: integer
+    name: S1
+    is_nullable: true
+  - type: integer
+    name: S2
+    is_nullable: true
   rows:
   - [1, 2]
   - [3, 3]
@@ -145,10 +147,12 @@ box.execute("UPDATE OR IGNORE tj SET s2 = s2 + 1;")
 box.execute("SELECT s1, s2 FROM tj;")
 ---
 - metadata:
-  - name: S1
-    type: integer
-  - name: S2
-    type: integer
+  - type: integer
+    name: S1
+    is_nullable: true
+  - type: integer
+    name: S2
+    is_nullable: true
   rows:
   - [1, 2]
   - [3, 4]
diff --git a/test/sql/persistency.result b/test/sql/persistency.result
index f8f992c39..90c4b36a6 100644
--- a/test/sql/persistency.result
+++ b/test/sql/persistency.result
@@ -38,10 +38,12 @@ box.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -54,10 +56,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -69,10 +73,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -83,10 +89,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -97,10 +105,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -112,10 +122,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -125,10 +137,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -138,10 +152,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -152,10 +168,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -167,10 +185,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -182,10 +202,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -197,10 +219,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -227,10 +251,12 @@ box.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -243,10 +269,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -320,10 +348,12 @@ box.execute("CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW BEGIN IN
 box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
 ---
 - metadata:
-  - name: name
-    type: string
-  - name: opts
-    type: map
+  - type: string
+    name: name
+    is_nullable: false
+  - type: map
+    name: opts
+    is_nullable: false
   rows:
   - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW
         BEGIN INSERT INTO barfoo VALUES (''trigger test'', 9999); END'}]
@@ -340,8 +370,9 @@ box.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt W
 box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows:
   - [840]
   - [880]
@@ -359,10 +390,12 @@ test_run:cmd('restart server default');
 box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
 ---
 - metadata:
-  - name: name
-    type: string
-  - name: opts
-    type: map
+  - type: string
+    name: name
+    is_nullable: false
+  - type: map
+    name: opts
+    is_nullable: false
   rows:
   - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW
         BEGIN INSERT INTO barfoo VALUES (''trigger test'', 9999); END'}]
@@ -386,10 +419,12 @@ box.execute("SELECT * FROM barfoo WHERE foo = 9999");
 box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 ---
 - metadata:
-  - name: name
-    type: string
-  - name: opts
-    type: map
+  - type: string
+    name: name
+    is_nullable: false
+  - type: map
+    name: opts
+    is_nullable: false
   rows:
   - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW
         BEGIN INSERT INTO barfoo VALUES (''trigger test'', 9999); END'}]
@@ -409,10 +444,12 @@ box.execute("DROP TRIGGER tfoobar")
 box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 ---
 - metadata:
-  - name: name
-    type: string
-  - name: opts
-    type: map
+  - type: string
+    name: name
+    is_nullable: false
+  - type: map
+    name: opts
+    is_nullable: false
   rows: []
 ...
 -- prove barfoo2 still exists
@@ -447,8 +484,9 @@ box.execute("SELECT * FROM foobar");
 box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows:
   - [840]
   - [880]
diff --git a/test/sql/row-count.result b/test/sql/row-count.result
index 6bf74ed9b..23af45e39 100644
--- a/test/sql/row-count.result
+++ b/test/sql/row-count.result
@@ -295,14 +295,18 @@ box.execute("SELECT ROW_COUNT();")
 box.execute("EXPLAIN QUERY PLAN INSERT INTO t1 VALUES ('b'), ('c'), ('d');")
 ---
 - metadata:
-  - name: selectid
-    type: integer
-  - name: order
-    type: integer
-  - name: from
-    type: integer
-  - name: detail
-    type: text
+  - type: integer
+    name: selectid
+    is_nullable: false
+  - type: integer
+    name: order
+    is_nullable: false
+  - type: integer
+    name: from
+    is_nullable: false
+  - type: text
+    name: detail
+    is_nullable: false
   rows:
   - [0, 0, 0, 'SCAN TABLE T2 (~262144 rows)']
 ...
@@ -317,8 +321,9 @@ box.execute("SELECT ROW_COUNT();")
 box.execute('PRAGMA recursive_triggers')
 ---
 - metadata:
-  - name: recursive_triggers
-    type: integer
+  - type: integer
+    name: recursive_triggers
+    is_nullable: false
   rows:
   - [1]
 ...
diff --git a/test/sql/sql-debug.result b/test/sql/sql-debug.result
index b19075366..cdaed9a00 100644
--- a/test/sql/sql-debug.result
+++ b/test/sql/sql-debug.result
@@ -18,8 +18,9 @@ box.execute('PRAGMA parser_trace = 1')
 box.execute('PRAGMA parser_trace')
 ---
 - metadata:
-  - name: parser_trace
-    type: integer
+  - type: integer
+    name: parser_trace
+    is_nullable: false
   rows:
   - [1]
 ...
@@ -33,10 +34,12 @@ box.execute('PRAGMA parser_trace = '.. result[1][1])
 box.execute('PRAGMA')
 ---
 - metadata:
-  - name: pragma_name
-    type: text
-  - name: pragma_value
-    type: integer
+  - type: text
+    name: pragma_name
+    is_nullable: false
+  - type: integer
+    name: pragma_value
+    is_nullable: false
   rows:
   - ['count_changes', 0]
   - ['defer_foreign_keys', 0]
diff --git a/test/sql/transition.result b/test/sql/transition.result
index 9738092fe..bba4ddefc 100644
--- a/test/sql/transition.result
+++ b/test/sql/transition.result
@@ -35,10 +35,12 @@ box.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -51,10 +53,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -66,10 +70,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -80,10 +86,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -94,10 +102,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -109,10 +119,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -122,10 +134,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -135,10 +149,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -149,10 +165,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -164,10 +182,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -179,10 +199,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -194,10 +216,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -224,10 +248,12 @@ box.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -240,10 +266,12 @@ box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
 ---
 - metadata:
-  - name: BAR
-    type: string
-  - name: FOO
-    type: integer
+  - type: string
+    name: BAR
+    is_nullable: true
+  - type: integer
+    name: FOO
+    is_nullable: false
   - name: '42'
     type: integer
   - name: '''awesome'''
@@ -318,10 +346,12 @@ box.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
 box.execute("SELECT foo, bar FROM barfoo")
 ---
 - metadata:
-  - name: FOO
-    type: number
-  - name: BAR
-    type: string
+  - type: number
+    name: FOO
+    is_nullable: false
+  - type: string
+    name: BAR
+    is_nullable: true
   rows:
   - [1, 'foo']
   - [2, 'bar']
@@ -330,30 +360,36 @@ box.execute("SELECT foo, bar FROM barfoo")
 box.execute("SELECT foo, bar FROM barfoo WHERE foo==2")
 ---
 - metadata:
-  - name: FOO
-    type: number
-  - name: BAR
-    type: string
+  - type: number
+    name: FOO
+    is_nullable: false
+  - type: string
+    name: BAR
+    is_nullable: true
   rows:
   - [2, 'bar']
 ...
 box.execute("SELECT foo, bar FROM barfoo WHERE bar=='foobar'")
 ---
 - metadata:
-  - name: FOO
-    type: number
-  - name: BAR
-    type: string
+  - type: number
+    name: FOO
+    is_nullable: false
+  - type: string
+    name: BAR
+    is_nullable: true
   rows:
   - [1000, 'foobar']
 ...
 box.execute("SELECT foo, bar FROM barfoo WHERE foo>=2")
 ---
 - metadata:
-  - name: FOO
-    type: number
-  - name: BAR
-    type: string
+  - type: number
+    name: FOO
+    is_nullable: false
+  - type: string
+    name: BAR
+    is_nullable: true
   rows:
   - [2, 'bar']
   - [1000, 'foobar']
@@ -361,10 +397,12 @@ box.execute("SELECT foo, bar FROM barfoo WHERE foo>=2")
 box.execute("SELECT foo, bar FROM barfoo WHERE foo<=2")
 ---
 - metadata:
-  - name: FOO
-    type: number
-  - name: BAR
-    type: string
+  - type: number
+    name: FOO
+    is_nullable: false
+  - type: string
+    name: BAR
+    is_nullable: true
   rows:
   - [1, 'foo']
   - [2, 'bar']
diff --git a/test/sql/types.result b/test/sql/types.result
index 1ad52e8c7..eb1eac989 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -375,37 +375,42 @@ box.execute("INSERT INTO t1 VALUES (1, 1);")
 box.execute("SELECT a FROM t1 WHERE a IN (1.1, 2.1);")
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows: []
 ...
 box.execute("SELECT a FROM t1 WHERE a = 1.1;")
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows: []
 ...
 box.execute("SELECT a FROM t1 WHERE a = 1.0;")
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows:
   - [1]
 ...
 box.execute("SELECT a FROM t1 WHERE a > 1.1;")
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows: []
 ...
 box.execute("SELECT a FROM t1 WHERE a < 1.1;")
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows:
   - [1]
 ...
@@ -427,23 +432,26 @@ box.execute("INSERT INTO t1 VALUES (1, 1, 1);")
 box.execute("SELECT a FROM t1 WHERE a = 1.0 AND b > 0.5;")
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows:
   - [1]
 ...
 box.execute("SELECT a FROM t1 WHERE a = 1.5 AND b IS NULL;")
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows: []
 ...
 box.execute("SELECT a FROM t1 WHERE a IS NULL AND b IS NULL;")
 ---
 - metadata:
-  - name: A
-    type: integer
+  - type: integer
+    name: A
+    is_nullable: true
   rows: []
 ...
 box.space.T1:drop()
@@ -474,8 +482,9 @@ s:insert({ 1, 1 })
 box.execute("SELECT a FROM t1 WHERE a IN (1.1, 2.1);")
 ---
 - metadata:
-  - name: A
-    type: unsigned
+  - type: unsigned
+    name: A
+    is_nullable: false
   rows: []
 ...
 s:drop()
@@ -711,8 +720,9 @@ box.execute("INSERT INTO t VALUES (3, 18446744073709551613)")
 box.execute("SELECT i FROM t;")
 ---
 - metadata:
-  - name: I
-    type: integer
+  - type: integer
+    name: I
+    is_nullable: true
   rows:
   - [18446744073709551615]
   - [18446744073709551614]
@@ -721,16 +731,18 @@ box.execute("SELECT i FROM t;")
 box.execute("SELECT i FROM t WHERE i = 18446744073709551615;")
 ---
 - metadata:
-  - name: I
-    type: integer
+  - type: integer
+    name: I
+    is_nullable: true
   rows:
   - [18446744073709551615]
 ...
 box.execute("SELECT i FROM t WHERE i BETWEEN 18446744073709551613 AND 18446744073709551615;")
 ---
 - metadata:
-  - name: I
-    type: integer
+  - type: integer
+    name: I
+    is_nullable: true
   rows:
   - [18446744073709551615]
   - [18446744073709551614]
@@ -739,8 +751,9 @@ box.execute("SELECT i FROM t WHERE i BETWEEN 18446744073709551613 AND 1844674407
 box.execute("SELECT i FROM t ORDER BY i;")
 ---
 - metadata:
-  - name: I
-    type: integer
+  - type: integer
+    name: I
+    is_nullable: true
   rows:
   - [18446744073709551613]
   - [18446744073709551614]
@@ -754,8 +767,9 @@ box.execute("SELECT i FROM t ORDER BY -i;")
 box.execute("SELECT i FROM t ORDER BY i LIMIT 1;")
 ---
 - metadata:
-  - name: I
-    type: integer
+  - type: integer
+    name: I
+    is_nullable: true
   rows:
   - [18446744073709551613]
 ...
@@ -896,16 +910,18 @@ box.execute("CREATE INDEX i ON t(i);")
 box.execute("SELECT i FROM t WHERE i = 18446744073709551613;")
 ---
 - metadata:
-  - name: I
-    type: integer
+  - type: integer
+    name: I
+    is_nullable: true
   rows:
   - [18446744073709551613]
 ...
 box.execute("SELECT i FROM t WHERE i >= 18446744073709551613 ORDER BY i;")
 ---
 - metadata:
-  - name: I
-    type: integer
+  - type: integer
+    name: I
+    is_nullable: true
   rows:
   - [18446744073709551613]
 ...
@@ -916,8 +932,9 @@ box.execute("UPDATE t SET i = 18446744073709551615 WHERE i = 1844674407370955161
 box.execute("SELECT i FROM t;")
 ---
 - metadata:
-  - name: I
-    type: integer
+  - type: integer
+    name: I
+    is_nullable: true
   rows:
   - [18446744073709551615]
 ...
@@ -1072,8 +1089,9 @@ box.execute("INSERT INTO t1 VALUES (-3);")
 box.execute("SELECT id FROM t1;")
 ---
 - metadata:
-  - name: ID
-    type: unsigned
+  - type: unsigned
+    name: ID
+    is_nullable: false
   rows:
   - [0]
   - [1]
@@ -1361,16 +1379,18 @@ box.execute("CREATE INDEX iv ON t(v);")
 box.execute("SELECT v FROM t WHERE v = x'616263';")
 ---
 - metadata:
-  - name: V
-    type: varbinary
+  - type: varbinary
+    name: V
+    is_nullable: true
   rows:
   - ['abc']
 ...
 box.execute("SELECT v FROM t ORDER BY v;")
 ---
 - metadata:
-  - name: V
-    type: varbinary
+  - type: varbinary
+    name: V
+    is_nullable: true
   rows:
   - ['abc']
 ...
@@ -1381,8 +1401,9 @@ box.execute("UPDATE t SET v = x'636261' WHERE v = x'616263';")
 box.execute("SELECT v FROM t;")
 ---
 - metadata:
-  - name: V
-    type: varbinary
+  - type: varbinary
+    name: V
+    is_nullable: true
   rows:
   - ['cba']
 ...
diff --git a/test/sql/update-with-nested-select.result b/test/sql/update-with-nested-select.result
index 31724307f..84b2d79aa 100644
--- a/test/sql/update-with-nested-select.result
+++ b/test/sql/update-with-nested-select.result
@@ -34,8 +34,9 @@ box.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
 box.execute("SELECT e FROM t1");
 ---
 - metadata:
-  - name: E
-    type: integer
+  - type: integer
+    name: E
+    is_nullable: true
   rows:
   - [7]
   - [8]
-- 
2.15.1



More information about the Tarantool-patches mailing list