Tarantool development patches archive
 help / color / mirror / Atom feed
From: Mergen Imeev via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: v.shpilevoy@tarantool.org, tsafin@tarantool.org
Cc: tarantool-patches@dev.tarantool.org
Subject: [Tarantool-patches] [PATCH v5 05/52] sql: move MEM-related functions to mem.c/mem.h
Date: Fri,  9 Apr 2021 19:51:36 +0300	[thread overview]
Message-ID: <16b3a31eb6de710a3512d38a40ca8fd52bb50bca.1617984948.git.imeevma@gmail.com> (raw)
In-Reply-To: <cover.1617984948.git.imeevma@gmail.com>

Thaks you for the review! My answers and new patch below.

On 30.03.2021 01:58, Vladislav Shpilevoy wrote:
> Thanks for the patch!
>
> I didn't compare each line of each moved function with its
> original code, so I trust you didn't make any functional
> changes.
>
> See 2 comments below.
>
>> diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
>> new file mode 100644
>> index 000000000..62338e1db
>> --- /dev/null
>> +++ b/src/box/sql/mem.c
>> @@ -0,0 +1,2376 @@
>
> <...>
>
>> +
>> +
>> +#ifdef SQL_DEBUG
>> +/*
>> + * This routine prepares a memory cell for modification by breaking
>> + * its link to a shallow copy and by marking any current shallow
>> + * copies of this cell as invalid.
>> + *
>> + * This is used for testing and debugging only - to make sure shallow
>> + * copies are not misused.
>> + */
>> +void
>> +sqlVdbeMemAboutToChange(Vdbe * pVdbe, Mem * pMem)
>
> 1. This is a vdbe method, not mem. It takes mem as an argument, but
> is a method of vdbe struct. Lets keep it in the old place.
>
Fixed. Moved it back and in patch "sql: disable unused code in sql/vdbemem.c"
moved it to vdbe.c.

>> +{
>> +  int i;
>> +  Mem *pX;
>> +  for (i = 0, pX = pVdbe->aMem; i < pVdbe->nMem; i++, pX++) {
>> +    if (pX->pScopyFrom == pMem) {
>> +      pX->flags |= MEM_Undefined;
>> +      pX->pScopyFrom = 0;
>> +    }
>> +  }
>> +  pMem->pScopyFrom = 0;
>> +}
>
> <...>
>
>> +
>> +/*
>> + * Print the SQL that was used to generate a VDBE program.
>> + */
>> +void
>> +sqlVdbePrintSql(Vdbe * p)
>
> 2. Ditto. Such functions should be moved to vdbe.h/vdbe.c eventually.
> And ideally mem.h/mem.c should not know about vdbe anything (probably
> can't be done now).
>
Fixed. Moved it back to vdbe.c. I think mem.c need vdbeInt.h and sqlInt.h right
now, so we can't make it forget about struct Vdbe. I hope this will change in
future.

>> +{
>> +  const char *z = 0;
>> +  if (p->zSql) {
>> +    z = p->zSql;
>> +  } else if (p->nOp >= 1) {
>> +    const VdbeOp *pOp = &p->aOp[0];
>> +    if (pOp->opcode == OP_Init && pOp->p4.z != 0) {
>> +      z = pOp->p4.z;
>> +      while (sqlIsspace(*z))
>> +        z++;
>> +    }
>> +  }
>> +  if (z)
>> +    printf("SQL: [%s]\n", z);
>> +}


New patch:

commit 16b3a31eb6de710a3512d38a40ca8fd52bb50bca
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Mon Mar 1 18:13:06 2021 +0300

    sql: move MEM-related functions to mem.c/mem.h
    
    This patch moves all MEM-related functions in mem.c/mem.h.
    
    Part of #5818

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 19203f770..41c32468f 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -42,6 +42,7 @@ set(sql_sources
     sql/legacy.c
     sql/main.c
     sql/malloc.c
+    sql/mem.c
     sql/os.c
     sql/os_unix.c
     sql/parse_def.c
diff --git a/src/box/sql.c b/src/box/sql.c
index aa91f003f..790ca7f70 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -34,6 +34,7 @@
 #include "sql.h"
 #include "sql/sqlInt.h"
 #include "sql/tarantoolInt.h"
+#include "sql/mem.h"
 #include "sql/vdbeInt.h"
 
 #include "index.h"
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index a015f70cb..5a95caa39 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -114,6 +114,7 @@
 
 #include "sqlInt.h"
 #include "tarantoolInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 
 #if 0
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 521cc2622..6470e0300 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -45,6 +45,7 @@
  */
 #include <ctype.h>
 #include "sqlInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "tarantoolInt.h"
 #include "box/ck_constraint.h"
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 9b6179f3a..46814f341 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -35,6 +35,7 @@
  * time functions, are implemented separately.)
  */
 #include "sqlInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "version.h"
 #include "coll/coll.h"
@@ -46,12 +47,6 @@
 #include <unicode/ucol.h>
 #include "box/coll_id_cache.h"
 #include "box/schema.h"
-#include "box/func.h"
-#include "box/port.h"
-#include "box/tuple.h"
-#include "lua/msgpack.h"
-#include "lua/utils.h"
-#include "mpstream/mpstream.h"
 
 /*
  * Return the collating function associated with a function.
@@ -77,308 +72,6 @@ sqlSkipAccumulatorLoad(sql_context * context)
  context->skipFlag = 1;
 }
 
-/**
- * Allocate a sequence of initialized vdbe memory registers
- * on region.
- */
-static struct Mem *
-vdbemem_alloc_on_region(uint32_t count)
-{
- struct region *region = &fiber()->gc;
- size_t size;
- struct Mem *ret = region_alloc_array(region, typeof(*ret), count,
-              &size);
- if (ret == NULL) {
-   diag_set(OutOfMemory, size, "region_alloc_array", "ret");
-   return NULL;
- }
- memset(ret, 0, count * sizeof(*ret));
- for (uint32_t i = 0; i < count; i++) {
-   sqlVdbeMemInit(&ret[i], sql_get(), MEM_Null);
-   assert(memIsValid(&ret[i]));
- }
- return ret;
-}
-
-static void
-port_vdbemem_dump_lua(struct port *base, struct lua_State *L, bool is_flat)
-{
- (void) is_flat;
- struct port_vdbemem *port = (struct port_vdbemem *) base;
- assert(is_flat == true);
- for (uint32_t i = 0; i < port->mem_count; i++) {
-   sql_value *param =
-     (sql_value *)((struct Mem *)port->mem + i);
-   switch (sql_value_type(param)) {
-   case MP_INT:
-     luaL_pushint64(L, sql_value_int64(param));
-     break;
-   case MP_UINT:
-     luaL_pushuint64(L, sql_value_uint64(param));
-     break;
-   case MP_DOUBLE:
-     lua_pushnumber(L, sql_value_double(param));
-     break;
-   case MP_STR:
-     lua_pushlstring(L, (const char *)sql_value_text(param),
-         (size_t)sql_value_bytes(param));
-     break;
-   case MP_BIN:
-   case MP_ARRAY:
-   case MP_MAP:
-     lua_pushlstring(L, sql_value_blob(param),
-         (size_t) sql_value_bytes(param));
-     break;
-   case MP_NIL:
-     lua_pushnil(L);
-     break;
-   case MP_BOOL:
-     lua_pushboolean(L, sql_value_boolean(param));
-     break;
-   default:
-     unreachable();
-   }
- }
-}
-
-static const char *
-port_vdbemem_get_msgpack(struct port *base, uint32_t *size)
-{
- struct port_vdbemem *port = (struct port_vdbemem *) base;
- struct region *region = &fiber()->gc;
- size_t region_svp = region_used(region);
- bool is_error = false;
- struct mpstream stream;
- mpstream_init(&stream, region, region_reserve_cb, region_alloc_cb,
-         set_encode_error, &is_error);
- mpstream_encode_array(&stream, port->mem_count);
- for (uint32_t i = 0; i < port->mem_count && !is_error; i++) {
-   sql_value *param =
-     (sql_value *)((struct Mem *)port->mem + i);
-   switch (sql_value_type(param)) {
-   case MP_INT: {
-     sql_int64 val = sql_value_int64(param);
-     if (val < 0) {
-       mpstream_encode_int(&stream, val);
-       break;
-     }
-     FALLTHROUGH;
-   }
-   case MP_UINT: {
-     sql_uint64 val = sql_value_uint64(param);
-     mpstream_encode_uint(&stream, val);
-     break;
-   }
-   case MP_DOUBLE: {
-     mpstream_encode_double(&stream,
-                sql_value_double(param));
-     break;
-   }
-   case MP_STR: {
-     const char *str = (const char *) sql_value_text(param);
-     mpstream_encode_strn(&stream, str,
-              sql_value_bytes(param));
-     break;
-   }
-   case MP_BIN:
-   case MP_ARRAY:
-   case MP_MAP: {
-     mpstream_encode_binl(&stream, sql_value_bytes(param));
-     mpstream_memcpy(&stream, sql_value_blob(param),
-         sql_value_bytes(param));
-     break;
-   }
-   case MP_NIL: {
-     mpstream_encode_nil(&stream);
-     break;
-   }
-   case MP_BOOL: {
-     mpstream_encode_bool(&stream, sql_value_boolean(param));
-     break;
-   }
-   default:
-     unreachable();
-   }
- }
- mpstream_flush(&stream);
- *size = region_used(region) - region_svp;
- if (is_error)
-   goto error;
- const char *ret = (char *)region_join(region, *size);
- if (ret == NULL)
-   goto error;
- return ret;
-error:
- diag_set(OutOfMemory, *size, "region", "ret");
- return NULL;
-}
-
-static const struct port_vtab port_vdbemem_vtab;
-
-void
-port_vdbemem_create(struct port *base, struct sql_value *mem,
-       uint32_t mem_count)
-{
- struct port_vdbemem *port = (struct port_vdbemem *) base;
- port->vtab = &port_vdbemem_vtab;
- port->mem = mem;
- port->mem_count = mem_count;
-}
-
-static struct sql_value *
-port_vdbemem_get_vdbemem(struct port *base, uint32_t *mem_count)
-{
- struct port_vdbemem *port = (struct port_vdbemem *) base;
- assert(port->vtab == &port_vdbemem_vtab);
- *mem_count = port->mem_count;
- return port->mem;
-}
-
-static const struct port_vtab port_vdbemem_vtab = {
- .dump_msgpack = NULL,
- .dump_msgpack_16 = NULL,
- .dump_lua = port_vdbemem_dump_lua,
- .dump_plain = NULL,
- .get_msgpack = port_vdbemem_get_msgpack,
- .get_vdbemem = port_vdbemem_get_vdbemem,
- .destroy = NULL,
-};
-
-struct sql_value *
-port_lua_get_vdbemem(struct port *base, uint32_t *size)
-{
- struct port_lua *port = (struct port_lua *) base;
- struct lua_State *L = port->L;
- int argc = lua_gettop(L);
- if (argc == 0 || argc > 1) {
-   diag_set(ClientError, ER_SQL_FUNC_WRONG_RET_COUNT, "Lua", argc);
-   return NULL;
- }
- *size = argc;
- /** FIXME: Implement an ability to return a vector. */
- assert(*size == 1);
- struct region *region = &fiber()->gc;
- size_t region_svp = region_used(region);
- struct Mem *val = vdbemem_alloc_on_region(argc);
- if (val == NULL)
-   return NULL;
- for (int i = 0; i < argc; i++) {
-   struct luaL_field field;
-   if (luaL_tofield(L, luaL_msgpack_default,
-        NULL, -1 - i, &field) < 0) {
-     goto error;
-   }
-   switch (field.type) {
-   case MP_BOOL:
-     mem_set_bool(&val[i], field.bval);
-     break;
-   case MP_FLOAT:
-     mem_set_double(&val[i], field.fval);
-     break;
-   case MP_DOUBLE:
-     mem_set_double(&val[i], field.dval);
-     break;
-   case MP_INT:
-     mem_set_i64(&val[i], field.ival);
-     break;
-   case MP_UINT:
-     mem_set_u64(&val[i], field.ival);
-     break;
-   case MP_STR:
-     if (sqlVdbeMemSetStr(&val[i], field.sval.data,
-              field.sval.len, 1,
-              SQL_TRANSIENT) != 0)
-       goto error;
-     break;
-   case MP_NIL:
-     sqlVdbeMemSetNull(&val[i]);
-     break;
-   default:
-     diag_set(ClientError, ER_SQL_EXECUTE,
-        "Unsupported type passed from Lua");
-     goto error;
-   }
- }
- return (struct sql_value *)val;
-error:
- for (int i = 0; i < argc; i++)
-   sqlVdbeMemRelease(&val[i]);
- region_truncate(region, region_svp);
- return NULL;
-}
-
-struct sql_value *
-port_c_get_vdbemem(struct port *base, uint32_t *size)
-{
- struct port_c *port = (struct port_c *)base;
- *size = port->size;
- if (*size == 0 || *size > 1) {
-   diag_set(ClientError, ER_SQL_FUNC_WRONG_RET_COUNT, "C", *size);
-   return NULL;
- }
- /** FIXME: Implement an ability to return a vector. */
- assert(*size == 1);
- struct region *region = &fiber()->gc;
- size_t region_svp = region_used(region);
- struct Mem *val = vdbemem_alloc_on_region(port->size);
- if (val == NULL)
-   return NULL;
- int i = 0;
- const char *data;
- struct port_c_entry *pe;
- for (pe = port->first; pe != NULL; pe = pe->next) {
-   if (pe->mp_size == 0) {
-     data = tuple_data(pe->tuple);
-     if (mp_decode_array(&data) != 1) {
-       diag_set(ClientError, ER_SQL_EXECUTE,
-          "Unsupported type passed from C");
-       goto error;
-     }
-   } else {
-     data = pe->mp;
-   }
-   uint32_t len;
-   const char *str;
-   switch (mp_typeof(*data)) {
-   case MP_BOOL:
-     mem_set_bool(&val[i], mp_decode_bool(&data));
-     break;
-   case MP_FLOAT:
-     mem_set_double(&val[i], mp_decode_float(&data));
-     break;
-   case MP_DOUBLE:
-     mem_set_double(&val[i], mp_decode_double(&data));
-     break;
-   case MP_INT:
-     mem_set_i64(&val[i], mp_decode_int(&data));
-     break;
-   case MP_UINT:
-     mem_set_u64(&val[i], mp_decode_uint(&data));
-     break;
-   case MP_STR:
-     str = mp_decode_str(&data, &len);
-     if (sqlVdbeMemSetStr(&val[i], str, len,
-              1, SQL_TRANSIENT) != 0)
-       goto error;
-     break;
-   case MP_NIL:
-     sqlVdbeMemSetNull(&val[i]);
-     break;
-   default:
-     diag_set(ClientError, ER_SQL_EXECUTE,
-        "Unsupported type passed from C");
-     goto error;
-   }
-   i++;
- }
- return (struct sql_value *) val;
-error:
- for (int i = 0; i < port->size; i++)
-   sqlVdbeMemRelease(&val[i]);
- region_truncate(region, region_svp);
- return NULL;
-}
-
 /*
  * Implementation of the non-aggregate min() and max() functions
  */
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 588e142d2..02e9f9673 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -35,6 +35,7 @@
  */
 #include "sqlInt.h"
 #include "tarantoolInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "box/ck_constraint.h"
 #include "bit/bit.h"
diff --git a/src/box/sql/main.c b/src/box/sql/main.c
index 0b20f2132..b0d32ae32 100644
--- a/src/box/sql/main.c
+++ b/src/box/sql/main.c
@@ -36,6 +36,7 @@
  * accessed by users of the library.
  */
 #include "sqlInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "version.h"
 #include "box/session.h"
diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
new file mode 100644
index 000000000..d28b9a958
--- /dev/null
+++ b/src/box/sql/mem.c
@@ -0,0 +1,2638 @@
+/*
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "sqlInt.h"
+#include "mem.h"
+#include "vdbeInt.h"
+#include "coll/coll.h"
+#include "tarantoolInt.h"
+#include "box/schema.h"
+#include "box/tuple.h"
+#include "mpstream/mpstream.h"
+#include "box/port.h"
+#include "lua/utils.h"
+#include "lua/msgpack.h"
+
+static inline bool
+mem_has_msgpack_subtype(struct Mem *mem)
+{
+ return (mem->flags & MEM_Subtype) != 0 &&
+        mem->subtype == SQL_SUBTYPE_MSGPACK;
+}
+
+/*
+ * The pVal argument is known to be a value other than NULL.
+ * Convert it into a string with encoding enc and return a pointer
+ * to a zero-terminated version of that string.
+ */
+static SQL_NOINLINE const void *
+valueToText(sql_value * pVal)
+{
+ assert(pVal != 0);
+ assert((pVal->flags & (MEM_Null)) == 0);
+ if ((pVal->flags & (MEM_Blob | MEM_Str)) &&
+     !mem_has_msgpack_subtype(pVal)) {
+   if (ExpandBlob(pVal))
+     return 0;
+   pVal->flags |= MEM_Str;
+   sqlVdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */
+ } else {
+   sqlVdbeMemStringify(pVal);
+   assert(0 == (1 & SQL_PTR_TO_INT(pVal->z)));
+ }
+ return pVal->z;
+}
+
+/**
+ * According to ANSI SQL string value can be converted to boolean
+ * type if string consists of literal "true" or "false" and
+ * number of leading and trailing spaces.
+ *
+ * For instance, "   tRuE  " can be successfully converted to
+ * boolean value true.
+ *
+ * @param str String to be converted to boolean. Assumed to be
+ *        null terminated.
+ * @param[out] result Resulting value of cast.
+ * @retval 0 If string satisfies conditions above.
+ * @retval -1 Otherwise.
+ */
+static int
+str_cast_to_boolean(const char *str, bool *result)
+{
+ assert(str != NULL);
+ for (; *str == ' '; str++);
+ if (strncasecmp(str, SQL_TOKEN_TRUE, strlen(SQL_TOKEN_TRUE)) == 0) {
+   *result = true;
+   str += 4;
+ } else if (strncasecmp(str, SQL_TOKEN_FALSE,
+            strlen(SQL_TOKEN_FALSE)) == 0) {
+   *result = false;
+   str += 5;
+ } else {
+   return -1;
+ }
+ for (; *str != '\0'; ++str) {
+   if (*str != ' ')
+     return -1;
+ }
+ return 0;
+}
+
+/*
+ * Convert a 64-bit IEEE double into a 64-bit signed integer.
+ * If the double is out of range of a 64-bit signed integer then
+ * return the closest available 64-bit signed integer.
+ */
+static int
+doubleToInt64(double r, int64_t *i)
+{
+ /*
+  * Many compilers we encounter do not define constants for the
+  * minimum and maximum 64-bit integers, or they define them
+  * inconsistently.  And many do not understand the "LL" notation.
+  * So we define our own static constants here using nothing
+  * larger than a 32-bit integer constant.
+  */
+ static const int64_t maxInt = LARGEST_INT64;
+ static const int64_t minInt = SMALLEST_INT64;
+ if (r <= (double)minInt) {
+   *i = minInt;
+   return -1;
+ } else if (r >= (double)maxInt) {
+   *i = maxInt;
+   return -1;
+ } else {
+   *i = (int64_t) r;
+   return *i != r;
+ }
+}
+
+/*
+ * It is already known that pMem contains an unterminated string.
+ * Add the zero terminator.
+ */
+static SQL_NOINLINE int
+vdbeMemAddTerminator(Mem * pMem)
+{
+ if (sqlVdbeMemGrow(pMem, pMem->n + 2, 1)) {
+   return -1;
+ }
+ pMem->z[pMem->n] = 0;
+ pMem->z[pMem->n + 1] = 0;
+ pMem->flags |= MEM_Term;
+ return 0;
+}
+
+/*
+ * If the memory cell contains a value that must be freed by
+ * invoking the external callback in Mem.xDel, then this routine
+ * will free that value.  It also sets Mem.flags to MEM_Null.
+ *
+ * This is a helper routine for sqlVdbeMemSetNull() and
+ * for sqlVdbeMemRelease().  Use those other routines as the
+ * entry point for releasing Mem resources.
+ */
+static SQL_NOINLINE void
+vdbeMemClearExternAndSetNull(Mem * p)
+{
+ assert(VdbeMemDynamic(p));
+ if (p->flags & MEM_Agg) {
+   sql_vdbemem_finalize(p, p->u.func);
+   assert((p->flags & MEM_Agg) == 0);
+   testcase(p->flags & MEM_Dyn);
+ }
+ if (p->flags & MEM_Dyn) {
+   assert(p->xDel != SQL_DYNAMIC && p->xDel != 0);
+   p->xDel((void *)p->z);
+ } else if (p->flags & MEM_Frame) {
+   VdbeFrame *pFrame = p->u.pFrame;
+   pFrame->pParent = pFrame->v->pDelFrame;
+   pFrame->v->pDelFrame = pFrame;
+ }
+ p->flags = MEM_Null;
+}
+
+/*
+ * Release memory held by the Mem p, both external memory cleared
+ * by p->xDel and memory in p->zMalloc.
+ *
+ * This is a helper routine invoked by sqlVdbeMemRelease() in
+ * the unusual case where there really is memory in p that needs
+ * to be freed.
+ */
+static SQL_NOINLINE void
+vdbeMemClear(Mem * p)
+{
+ if (VdbeMemDynamic(p)) {
+   vdbeMemClearExternAndSetNull(p);
+ }
+ if (p->szMalloc) {
+   sqlDbFree(p->db, p->zMalloc);
+   p->szMalloc = 0;
+ }
+ p->z = 0;
+}
+
+/*
+ * Make an shallow copy of pFrom into pTo.  Prior contents of
+ * pTo are freed.  The pFrom->z field is not duplicated.  If
+ * pFrom->z is used, then pTo->z points to the same thing as pFrom->z
+ * and flags gets srcType (either MEM_Ephem or MEM_Static).
+ */
+static SQL_NOINLINE void
+vdbeClrCopy(Mem * pTo, const Mem * pFrom, int eType)
+{
+ vdbeMemClearExternAndSetNull(pTo);
+ assert(!VdbeMemDynamic(pTo));
+ sqlVdbeMemShallowCopy(pTo, pFrom, eType);
+}
+
+/*
+ * Both *pMem1 and *pMem2 contain string values. Compare the two values
+ * using the collation sequence pColl. As usual, return a negative , zero
+ * or positive value if *pMem1 is less than, equal to or greater than
+ * *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
+ *
+ * Strungs assume to be UTF-8 encoded
+ */
+static int
+vdbeCompareMemString(const Mem * pMem1, const Mem * pMem2,
+        const struct coll * pColl)
+{
+ return pColl->cmp(pMem1->z, (size_t)pMem1->n,
+           pMem2->z, (size_t)pMem2->n, pColl);
+}
+
+/*
+ * The input pBlob is guaranteed to be a Blob that is not marked
+ * with MEM_Zero.  Return true if it could be a zero-blob.
+ */
+static int
+isAllZero(const char *z, int n)
+{
+ int i;
+ for (i = 0; i < n; i++) {
+   if (z[i])
+     return 0;
+ }
+ return 1;
+}
+
+char *
+mem_type_to_str(const struct Mem *p)
+{
+ assert(p != NULL);
+ switch (p->flags & MEM_PURE_TYPE_MASK) {
+ case MEM_Null:
+   return "NULL";
+ case MEM_Str:
+   return "text";
+ case MEM_Int:
+   return "integer";
+ case MEM_UInt:
+   return "unsigned";
+ case MEM_Real:
+   return "real";
+ case MEM_Blob:
+   return "varbinary";
+ case MEM_Bool:
+   return "boolean";
+ default:
+   unreachable();
+ }
+}
+
+enum mp_type
+mem_mp_type(struct Mem *mem)
+{
+ switch (mem->flags & MEM_PURE_TYPE_MASK) {
+ case MEM_Int:
+   return MP_INT;
+ case MEM_UInt:
+   return MP_UINT;
+ case MEM_Real:
+   return MP_DOUBLE;
+ case MEM_Str:
+   return MP_STR;
+ case MEM_Blob:
+   if ((mem->flags & MEM_Subtype) == 0 ||
+        mem->subtype != SQL_SUBTYPE_MSGPACK)
+     return MP_BIN;
+   assert(mp_typeof(*mem->z) == MP_MAP ||
+          mp_typeof(*mem->z) == MP_ARRAY);
+   return mp_typeof(*mem->z);
+ case MEM_Bool:
+   return MP_BOOL;
+ case MEM_Null:
+   return MP_NIL;
+ default: unreachable();
+ }
+}
+
+/* EVIDENCE-OF: R-12793-43283 Every value in sql has one of five
+ * fundamental datatypes: 64-bit signed integer 64-bit IEEE floating
+ * point number string BLOB NULL
+ */
+enum mp_type
+sql_value_type(sql_value *pVal)
+{
+ struct Mem *mem = (struct Mem *) pVal;
+ return mem_mp_type(mem);
+}
+
+
+/*
+ * pMem currently only holds a string type (or maybe a BLOB that we can
+ * interpret as a string if we want to).  Compute its corresponding
+ * numeric type, if has one.  Set the pMem->u.r and pMem->u.i fields
+ * accordingly.
+ */
+static u16 SQL_NOINLINE
+computeNumericType(Mem *pMem)
+{
+ assert((pMem->flags & (MEM_Int | MEM_UInt | MEM_Real)) == 0);
+ assert((pMem->flags & (MEM_Str|MEM_Blob))!=0);
+ if (sqlAtoF(pMem->z, &pMem->u.r, pMem->n)==0)
+   return 0;
+ bool is_neg;
+ if (sql_atoi64(pMem->z, (int64_t *) &pMem->u.i, &is_neg, pMem->n) == 0)
+   return is_neg ? MEM_Int : MEM_UInt;
+ return MEM_Real;
+}
+
+/*
+ * Return the numeric type for pMem, either MEM_Int or MEM_Real or both or
+ * none.
+ *
+ * Unlike mem_apply_numeric_type(), this routine does not modify pMem->flags.
+ * But it does set pMem->u.r and pMem->u.i appropriately.
+ */
+u16
+numericType(Mem *pMem)
+{
+ if ((pMem->flags & (MEM_Int | MEM_UInt | MEM_Real)) != 0)
+   return pMem->flags & (MEM_Int | MEM_UInt | MEM_Real);
+ if (pMem->flags & (MEM_Str|MEM_Blob)) {
+   return computeNumericType(pMem);
+ }
+ return 0;
+}
+
+/*
+ * The sqlValueBytes() routine returns the number of bytes in the
+ * sql_value object assuming that it uses the encoding "enc".
+ * The valueBytes() routine is a helper function.
+ */
+static SQL_NOINLINE int
+valueBytes(sql_value * pVal)
+{
+ return valueToText(pVal) != 0 ? pVal->n : 0;
+}
+
+int
+sqlValueBytes(sql_value * pVal)
+{
+ Mem *p = (Mem *) pVal;
+ assert((p->flags & MEM_Null) == 0
+        || (p->flags & (MEM_Str | MEM_Blob)) == 0);
+ if ((p->flags & MEM_Str) != 0) {
+   return p->n;
+ }
+ if ((p->flags & MEM_Blob) != 0) {
+   if (p->flags & MEM_Zero) {
+     return p->n + p->u.nZero;
+   } else {
+     return p->n;
+   }
+ }
+ if (p->flags & MEM_Null)
+   return 0;
+ return valueBytes(pVal);
+}
+
+
+#ifdef SQL_DEBUG
+/*
+ * Check invariants on a Mem object.
+ *
+ * This routine is intended for use inside of assert() statements, like
+ * this:    assert( sqlVdbeCheckMemInvariants(pMem) );
+ */
+int
+sqlVdbeCheckMemInvariants(Mem * p)
+{
+ /* If MEM_Dyn is set then Mem.xDel!=0.
+  * Mem.xDel is might not be initialized if MEM_Dyn is clear.
+  */
+ assert((p->flags & MEM_Dyn) == 0 || p->xDel != 0);
+
+ /* MEM_Dyn may only be set if Mem.szMalloc==0.  In this way we
+  * ensure that if Mem.szMalloc>0 then it is safe to do
+  * Mem.z = Mem.zMalloc without having to check Mem.flags&MEM_Dyn.
+  * That saves a few cycles in inner loops.
+  */
+ assert((p->flags & MEM_Dyn) == 0 || p->szMalloc == 0);
+
+ /* Cannot be both MEM_Int and MEM_Real at the same time */
+ assert((p->flags & (MEM_Int | MEM_Real)) != (MEM_Int | MEM_Real));
+ /* Can't be both UInt and Int at the same time.  */
+ assert((p->flags & (MEM_Int | MEM_UInt)) != (MEM_Int | MEM_UInt));
+
+ /* The szMalloc field holds the correct memory allocation size */
+ assert(p->szMalloc == 0 ||
+        p->szMalloc == sqlDbMallocSize(p->db, p->zMalloc));
+
+ /* If p holds a string or blob, the Mem.z must point to exactly
+  * one of the following:
+  *
+  *   (1) Memory in Mem.zMalloc and managed by the Mem object
+  *   (2) Memory to be freed using Mem.xDel
+  *   (3) An ephemeral string or blob
+  *   (4) A static string or blob
+  */
+ if ((p->flags & (MEM_Str | MEM_Blob)) && p->n > 0) {
+   assert(((p->szMalloc > 0 && p->z == p->zMalloc) ? 1 : 0) +
+          ((p->flags & MEM_Dyn) != 0 ? 1 : 0) +
+          ((p->flags & MEM_Ephem) != 0 ? 1 : 0) +
+          ((p->flags & MEM_Static) != 0 ? 1 : 0) == 1);
+ }
+ return 1;
+}
+
+/*
+ * Write a nice string representation of the contents of cell pMem
+ * into buffer zBuf, length nBuf.
+ */
+void
+sqlVdbeMemPrettyPrint(Mem *pMem, char *zBuf)
+{
+ char *zCsr = zBuf;
+ int f = pMem->flags;
+
+ if (f&MEM_Blob) {
+   int i;
+   char c;
+   if (f & MEM_Dyn) {
+     c = 'z';
+     assert((f & (MEM_Static|MEM_Ephem))==0);
+   } else if (f & MEM_Static) {
+     c = 't';
+     assert((f & (MEM_Dyn|MEM_Ephem))==0);
+   } else if (f & MEM_Ephem) {
+     c = 'e';
+     assert((f & (MEM_Static|MEM_Dyn))==0);
+   } else {
+     c = 's';
+   }
+
+   sql_snprintf(100, zCsr, "%c", c);
+   zCsr += sqlStrlen30(zCsr);
+   sql_snprintf(100, zCsr, "%d[", pMem->n);
+   zCsr += sqlStrlen30(zCsr);
+   for(i=0; i<16 && i<pMem->n; i++) {
+     sql_snprintf(100, zCsr, "%02X", ((int)pMem->z[i] & 0xFF));
+     zCsr += sqlStrlen30(zCsr);
+   }
+   for(i=0; i<16 && i<pMem->n; i++) {
+     char z = pMem->z[i];
+     if (z<32 || z>126) *zCsr++ = '.';
+     else *zCsr++ = z;
+   }
+   sql_snprintf(100, zCsr, "]%s", "(8)");
+   zCsr += sqlStrlen30(zCsr);
+   if (f & MEM_Zero) {
+     sql_snprintf(100, zCsr,"+%dz",pMem->u.nZero);
+     zCsr += sqlStrlen30(zCsr);
+   }
+   *zCsr = '\0';
+ } else if (f & MEM_Str) {
+   int j, k;
+   zBuf[0] = ' ';
+   if (f & MEM_Dyn) {
+     zBuf[1] = 'z';
+     assert((f & (MEM_Static|MEM_Ephem))==0);
+   } else if (f & MEM_Static) {
+     zBuf[1] = 't';
+     assert((f & (MEM_Dyn|MEM_Ephem))==0);
+   } else if (f & MEM_Ephem) {
+     zBuf[1] = 'e';
+     assert((f & (MEM_Static|MEM_Dyn))==0);
+   } else {
+     zBuf[1] = 's';
+   }
+   k = 2;
+   sql_snprintf(100, &zBuf[k], "%d", pMem->n);
+   k += sqlStrlen30(&zBuf[k]);
+   zBuf[k++] = '[';
+   for(j=0; j<15 && j<pMem->n; j++) {
+     u8 c = pMem->z[j];
+     if (c>=0x20 && c<0x7f) {
+       zBuf[k++] = c;
+     } else {
+       zBuf[k++] = '.';
+     }
+   }
+   zBuf[k++] = ']';
+   sql_snprintf(100,&zBuf[k],"(8)");
+   k += sqlStrlen30(&zBuf[k]);
+   zBuf[k++] = 0;
+ }
+}
+
+/*
+ * Print the value of a register for tracing purposes:
+ */
+static void
+memTracePrint(Mem *p)
+{
+ if (p->flags & MEM_Undefined) {
+   printf(" undefined");
+ } else if (p->flags & MEM_Null) {
+   printf(" NULL");
+ } else if ((p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str)) {
+   printf(" si:%lld", p->u.i);
+ } else if (p->flags & MEM_Int) {
+   printf(" i:%lld", p->u.i);
+ } else if (p->flags & MEM_UInt) {
+   printf(" u:%"PRIu64"", p->u.u);
+ } else if (p->flags & MEM_Real) {
+   printf(" r:%g", p->u.r);
+ } else if (p->flags & MEM_Bool) {
+   printf(" bool:%s", SQL_TOKEN_BOOLEAN(p->u.b));
+ } else {
+   char zBuf[200];
+   sqlVdbeMemPrettyPrint(p, zBuf);
+   printf(" %s", zBuf);
+ }
+ if (p->flags & MEM_Subtype) printf(" subtype=0x%02x", p->subtype);
+}
+
+void
+registerTrace(int iReg, Mem *p) {
+ printf("REG[%d] = ", iReg);
+ memTracePrint(p);
+ printf("\n");
+}
+#endif
+
+int
+mem_apply_numeric_type(struct Mem *record)
+{
+ if ((record->flags & MEM_Str) == 0)
+   return -1;
+ int64_t integer_value;
+ bool is_neg;
+ if (sql_atoi64(record->z, &integer_value, &is_neg, record->n) == 0) {
+   mem_set_int(record, integer_value, is_neg);
+   return 0;
+ }
+ double float_value;
+ if (sqlAtoF(record->z, &float_value, record->n) == 0)
+   return -1;
+ mem_set_double(record, float_value);
+ return 0;
+}
+
+/*
+ * Convert pMem so that it is of type MEM_Real.
+ * Invalidate any prior representations.
+ */
+int
+sqlVdbeMemRealify(Mem * pMem)
+{
+ assert(EIGHT_BYTE_ALIGNMENT(pMem));
+ double v;
+ if (sqlVdbeRealValue(pMem, &v))
+   return -1;
+ mem_set_double(pMem, v);
+ return 0;
+}
+
+int
+vdbe_mem_numerify(struct Mem *mem)
+{
+ if ((mem->flags & (MEM_Int | MEM_UInt | MEM_Real | MEM_Null)) != 0)
+   return 0;
+ if ((mem->flags & MEM_Bool) != 0) {
+   mem->u.u = mem->u.b;
+   MemSetTypeFlag(mem, MEM_UInt);
+   return 0;
+ }
+ assert((mem->flags & (MEM_Blob | MEM_Str)) != 0);
+ bool is_neg;
+ int64_t i;
+ if (sql_atoi64(mem->z, &i, &is_neg, mem->n) == 0) {
+   mem_set_int(mem, i, is_neg);
+ } else {
+   double d;
+   if (sqlAtoF(mem->z, &d, mem->n) == 0)
+     return -1;
+   mem_set_double(mem, d);
+ }
+ return 0;
+}
+
+/*
+ * Cast the datatype of the value in pMem according to the type
+ * @type.  Casting is different from applying type in that a cast
+ * is forced.  In other words, the value is converted into the desired
+ * type even if that results in loss of data.  This routine is
+ * used (for example) to implement the SQL "cast()" operator.
+ */
+int
+sqlVdbeMemCast(Mem * pMem, enum field_type type)
+{
+ assert(type < field_type_MAX);
+ if (pMem->flags & MEM_Null)
+   return 0;
+ switch (type) {
+ case FIELD_TYPE_SCALAR:
+   return 0;
+ case FIELD_TYPE_BOOLEAN:
+   if ((pMem->flags & MEM_Int) != 0) {
+     mem_set_bool(pMem, pMem->u.i);
+     return 0;
+   }
+   if ((pMem->flags & MEM_UInt) != 0) {
+     mem_set_bool(pMem, pMem->u.u);
+     return 0;
+   }
+   if ((pMem->flags & MEM_Real) != 0) {
+     mem_set_bool(pMem, pMem->u.r);
+     return 0;
+   }
+   if ((pMem->flags & MEM_Str) != 0) {
+     bool value;
+     if (str_cast_to_boolean(pMem->z, &value) != 0)
+       return -1;
+     mem_set_bool(pMem, value);
+     return 0;
+   }
+   if ((pMem->flags & MEM_Bool) != 0)
+     return 0;
+   return -1;
+ case FIELD_TYPE_INTEGER:
+ case FIELD_TYPE_UNSIGNED:
+   if ((pMem->flags & (MEM_Blob | MEM_Str)) != 0) {
+     bool is_neg;
+     int64_t val;
+     if (sql_atoi64(pMem->z, &val, &is_neg, pMem->n) != 0)
+       return -1;
+     if (type == FIELD_TYPE_UNSIGNED && is_neg)
+       return -1;
+     mem_set_int(pMem, val, is_neg);
+     return 0;
+   }
+   if ((pMem->flags & MEM_Bool) != 0) {
+     pMem->u.u = pMem->u.b;
+     MemSetTypeFlag(pMem, MEM_UInt);
+     return 0;
+   }
+   if ((pMem->flags & MEM_Real) != 0) {
+     double d;
+     if (sqlVdbeRealValue(pMem, &d) != 0)
+       return -1;
+     if (d < (double)INT64_MAX && d >= (double)INT64_MIN) {
+       mem_set_int(pMem, d, d <= -1);
+       return 0;
+     }
+     if (d >= (double)INT64_MAX && d < (double)UINT64_MAX) {
+       mem_set_u64(pMem, d);
+       return 0;
+     }
+     return -1;
+   }
+   if (type == FIELD_TYPE_UNSIGNED &&
+       (pMem->flags & MEM_UInt) == 0)
+     return -1;
+   return 0;
+ case FIELD_TYPE_DOUBLE:
+   return sqlVdbeMemRealify(pMem);
+ case FIELD_TYPE_NUMBER:
+   return vdbe_mem_numerify(pMem);
+ case FIELD_TYPE_VARBINARY:
+   if ((pMem->flags & MEM_Blob) != 0)
+     return 0;
+   if ((pMem->flags & MEM_Str) != 0) {
+     MemSetTypeFlag(pMem, MEM_Str);
+     return 0;
+   }
+   return -1;
+ default:
+   assert(type == FIELD_TYPE_STRING);
+   assert(MEM_Str == (MEM_Blob >> 3));
+   if ((pMem->flags & MEM_Bool) != 0) {
+     const char *str_bool = SQL_TOKEN_BOOLEAN(pMem->u.b);
+     sqlVdbeMemSetStr(pMem, str_bool, strlen(str_bool), 1,
+          SQL_TRANSIENT);
+     return 0;
+   }
+   pMem->flags |= (pMem->flags & MEM_Blob) >> 3;
+     sql_value_apply_type(pMem, FIELD_TYPE_STRING);
+   assert(pMem->flags & MEM_Str || pMem->db->mallocFailed);
+   pMem->flags &=
+     ~(MEM_Int | MEM_UInt | MEM_Real | MEM_Blob | MEM_Zero);
+   return 0;
+ }
+}
+
+/*
+ * The MEM structure is already a MEM_Real.  Try to also make it a
+ * MEM_Int if we can.
+ */
+int
+mem_apply_integer_type(Mem *pMem)
+{
+ int rc;
+ i64 ix;
+ assert(pMem->flags & MEM_Real);
+ assert(EIGHT_BYTE_ALIGNMENT(pMem));
+
+ if ((rc = doubleToInt64(pMem->u.r, (int64_t *) &ix)) == 0)
+   mem_set_int(pMem, ix, pMem->u.r <= -1);
+ return rc;
+}
+
+/*
+ * Add MEM_Str to the set of representations for the given Mem.  Numbers
+ * are converted using sql_snprintf().  Converting a BLOB to a string
+ * is a no-op.
+ *
+ * Existing representations MEM_Int and MEM_Real are invalidated if
+ * bForce is true but are retained if bForce is false.
+ *
+ * A MEM_Null value will never be passed to this function. This function is
+ * used for converting values to text for returning to the user (i.e. via
+ * sql_value_text()), or for ensuring that values to be used as btree
+ * keys are strings. In the former case a NULL pointer is returned the
+ * user and the latter is an internal programming error.
+ */
+int
+sqlVdbeMemStringify(Mem * pMem)
+{
+ int fg = pMem->flags;
+ int nByte = 32;
+
+ if ((fg & (MEM_Null | MEM_Str | MEM_Blob)) != 0 &&
+     !mem_has_msgpack_subtype(pMem))
+   return 0;
+
+ assert(!(fg & MEM_Zero));
+ assert((fg & (MEM_Int | MEM_UInt | MEM_Real | MEM_Bool |
+         MEM_Blob)) != 0);
+ assert(EIGHT_BYTE_ALIGNMENT(pMem));
+
+ /*
+  * In case we have ARRAY/MAP we should save decoded value
+  * before clearing pMem->z.
+  */
+ char *value = NULL;
+ if (mem_has_msgpack_subtype(pMem)) {
+   const char *value_str = mp_str(pMem->z);
+   nByte = strlen(value_str) + 1;
+   value = region_alloc(&fiber()->gc, nByte);
+   memcpy(value, value_str, nByte);
+ }
+
+ if (sqlVdbeMemClearAndResize(pMem, nByte)) {
+   return -1;
+ }
+ if (fg & MEM_Int) {
+   sql_snprintf(nByte, pMem->z, "%lld", pMem->u.i);
+   pMem->flags &= ~MEM_Int;
+ } else if ((fg & MEM_UInt) != 0) {
+   sql_snprintf(nByte, pMem->z, "%llu", pMem->u.u);
+   pMem->flags &= ~MEM_UInt;
+ } else if ((fg & MEM_Bool) != 0) {
+   sql_snprintf(nByte, pMem->z, "%s",
+          SQL_TOKEN_BOOLEAN(pMem->u.b));
+   pMem->flags &= ~MEM_Bool;
+ } else if (mem_has_msgpack_subtype(pMem)) {
+   sql_snprintf(nByte, pMem->z, "%s", value);
+   pMem->flags &= ~MEM_Subtype;
+   pMem->subtype = SQL_SUBTYPE_NO;
+ } else {
+   assert(fg & MEM_Real);
+   sql_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
+   pMem->flags &= ~MEM_Real;
+ }
+ pMem->n = sqlStrlen30(pMem->z);
+ pMem->flags |= MEM_Str | MEM_Term;
+ return 0;
+}
+
+/*
+ * Make sure the given Mem is \u0000 terminated.
+ */
+int
+sqlVdbeMemNulTerminate(Mem * pMem)
+{
+ testcase((pMem->flags & (MEM_Term | MEM_Str)) == (MEM_Term | MEM_Str));
+ testcase((pMem->flags & (MEM_Term | MEM_Str)) == 0);
+ if ((pMem->flags & (MEM_Term | MEM_Str)) != MEM_Str) {
+   return 0; /* Nothing to do */
+ } else {
+   return vdbeMemAddTerminator(pMem);
+ }
+}
+
+/*
+ * If the given Mem* has a zero-filled tail, turn it into an ordinary
+ * blob stored in dynamically allocated space.
+ */
+int
+sqlVdbeMemExpandBlob(Mem * pMem)
+{
+ int nByte;
+ assert(pMem->flags & MEM_Zero);
+ assert(pMem->flags & MEM_Blob);
+
+ /* Set nByte to the number of bytes required to store the expanded blob. */
+ nByte = pMem->n + pMem->u.nZero;
+ if (nByte <= 0) {
+   nByte = 1;
+ }
+ if (sqlVdbeMemGrow(pMem, nByte, 1)) {
+   return -1;
+ }
+
+ memset(&pMem->z[pMem->n], 0, pMem->u.nZero);
+ pMem->n += pMem->u.nZero;
+ pMem->flags &= ~(MEM_Zero | MEM_Term);
+ return 0;
+}
+
+/*
+ * Exported version of mem_apply_type(). This one works on sql_value*,
+ * not the internal Mem* type.
+ */
+void
+sql_value_apply_type(
+ sql_value *pVal,
+ enum field_type type)
+{
+ mem_apply_type((Mem *) pVal, type);
+}
+
+int
+mem_apply_type(struct Mem *record, enum field_type type)
+{
+ if ((record->flags & MEM_Null) != 0)
+   return 0;
+ assert(type < field_type_MAX);
+ switch (type) {
+ case FIELD_TYPE_INTEGER:
+ case FIELD_TYPE_UNSIGNED:
+   if ((record->flags & (MEM_Bool | MEM_Blob)) != 0)
+     return -1;
+   if ((record->flags & MEM_UInt) == MEM_UInt)
+     return 0;
+   if ((record->flags & MEM_Real) == MEM_Real) {
+     double d = record->u.r;
+     if (d >= 0) {
+       if (double_compare_uint64(d, UINT64_MAX,
+               1) > 0)
+         return 0;
+       if ((double)(uint64_t)d == d)
+         mem_set_u64(record, (uint64_t)d);
+     } else {
+       if (double_compare_nint64(d, INT64_MIN, 1) < 0)
+         return 0;
+       if ((double)(int64_t)d == d)
+         mem_set_int(record, (int64_t)d, true);
+     }
+     return 0;
+   }
+   if ((record->flags & MEM_Str) != 0) {
+     bool is_neg;
+     int64_t i;
+     if (sql_atoi64(record->z, &i, &is_neg, record->n) != 0)
+       return -1;
+     mem_set_int(record, i, is_neg);
+   }
+   if ((record->flags & MEM_Int) == MEM_Int) {
+     if (type == FIELD_TYPE_UNSIGNED)
+       return -1;
+     return 0;
+   }
+   return 0;
+ case FIELD_TYPE_BOOLEAN:
+   if ((record->flags & MEM_Bool) == MEM_Bool)
+     return 0;
+   return -1;
+ case FIELD_TYPE_NUMBER:
+   if ((record->flags & (MEM_Real | MEM_Int | MEM_UInt)) != 0)
+     return 0;
+   return sqlVdbeMemRealify(record);
+ case FIELD_TYPE_DOUBLE:
+   if ((record->flags & MEM_Real) != 0)
+     return 0;
+   return sqlVdbeMemRealify(record);
+ case FIELD_TYPE_STRING:
+   /*
+    * Only attempt the conversion to TEXT if there is
+    * an integer or real representation (BLOB and
+    * NULL do not get converted).
+    */
+   if ((record->flags & MEM_Str) == 0 &&
+       (record->flags & (MEM_Real | MEM_Int | MEM_UInt)) != 0)
+     sqlVdbeMemStringify(record);
+   record->flags &= ~(MEM_Real | MEM_Int | MEM_UInt);
+   return 0;
+ case FIELD_TYPE_VARBINARY:
+   if ((record->flags & MEM_Blob) == 0)
+     return -1;
+   return 0;
+ case FIELD_TYPE_SCALAR:
+   /* Can't cast MAP and ARRAY to scalar types. */
+   if ((record->flags & MEM_Subtype) != 0 &&
+       record->subtype == SQL_SUBTYPE_MSGPACK) {
+     assert(mp_typeof(*record->z) == MP_MAP ||
+            mp_typeof(*record->z) == MP_ARRAY);
+     return -1;
+   }
+   return 0;
+ case FIELD_TYPE_MAP:
+   if ((record->flags & MEM_Subtype) != 0 &&
+       record->subtype == SQL_SUBTYPE_MSGPACK &&
+       mp_typeof(*record->z) == MP_MAP)
+     return 0;
+   return -1;
+ case FIELD_TYPE_ARRAY:
+   if ((record->flags & MEM_Subtype) != 0 &&
+       record->subtype == SQL_SUBTYPE_MSGPACK &&
+       mp_typeof(*record->z) == MP_ARRAY)
+     return 0;
+   return -1;
+ case FIELD_TYPE_ANY:
+   return 0;
+ default:
+   return -1;
+ }
+}
+
+/**
+ * Convert the numeric value contained in MEM to double.
+ *
+ * @param mem The MEM that contains the numeric value.
+ * @retval 0 if the conversion was successful, -1 otherwise.
+ */
+static int
+mem_convert_to_double(struct Mem *mem)
+{
+ if ((mem->flags & MEM_Real) != 0)
+   return 0;
+ if ((mem->flags & (MEM_Int | MEM_UInt)) == 0)
+   return -1;
+ double d;
+ if ((mem->flags & MEM_Int) != 0)
+   d = (double)mem->u.i;
+ else
+   d = (double)mem->u.u;
+ mem_set_double(mem, d);
+ return 0;
+}
+
+/**
+ * Convert the numeric value contained in MEM to unsigned.
+ *
+ * @param mem The MEM that contains the numeric value.
+ * @retval 0 if the conversion was successful, -1 otherwise.
+ */
+static int
+mem_convert_to_unsigned(struct Mem *mem)
+{
+ if ((mem->flags & MEM_UInt) != 0)
+   return 0;
+ if ((mem->flags & MEM_Int) != 0)
+   return -1;
+ if ((mem->flags & MEM_Real) == 0)
+   return -1;
+ double d = mem->u.r;
+ if (d < 0.0 || d >= (double)UINT64_MAX)
+   return -1;
+ mem_set_u64(mem, (uint64_t) d);
+ return 0;
+}
+
+/**
+ * Convert the numeric value contained in MEM to integer.
+ *
+ * @param mem The MEM that contains the numeric value.
+ * @retval 0 if the conversion was successful, -1 otherwise.
+ */
+static int
+mem_convert_to_integer(struct Mem *mem)
+{
+ if ((mem->flags & (MEM_UInt | MEM_Int)) != 0)
+   return 0;
+ if ((mem->flags & MEM_Real) == 0)
+   return -1;
+ double d = mem->u.r;
+ if (d >= (double)UINT64_MAX || d < (double)INT64_MIN)
+   return -1;
+ if (d < (double)INT64_MAX)
+   mem_set_int(mem, (int64_t) d, d < 0);
+ else
+   mem_set_int(mem, (uint64_t) d, false);
+ return 0;
+}
+
+int
+mem_convert_to_numeric(struct Mem *mem, enum field_type type)
+{
+ assert(mp_type_is_numeric(mem_mp_type(mem)) &&
+        sql_type_is_numeric(type));
+ assert(type != FIELD_TYPE_NUMBER);
+ if (type == FIELD_TYPE_DOUBLE)
+   return mem_convert_to_double(mem);
+ if (type == FIELD_TYPE_UNSIGNED)
+   return mem_convert_to_unsigned(mem);
+ assert(type == FIELD_TYPE_INTEGER);
+ return mem_convert_to_integer(mem);
+}
+
+/*
+ * Make sure pMem->z points to a writable allocation of at least
+ * min(n,32) bytes.
+ *
+ * If the bPreserve argument is true, then copy of the content of
+ * pMem->z into the new allocation.  pMem must be either a string or
+ * blob if bPreserve is true.  If bPreserve is false, any prior content
+ * in pMem->z is discarded.
+ */
+SQL_NOINLINE int
+sqlVdbeMemGrow(Mem * pMem, int n, int bPreserve)
+{
+ assert(sqlVdbeCheckMemInvariants(pMem));
+ testcase(pMem->db == 0);
+
+ /* If the bPreserve flag is set to true, then the memory cell must already
+  * contain a valid string or blob value.
+  */
+ assert(bPreserve == 0 || pMem->flags & (MEM_Blob | MEM_Str));
+ testcase(bPreserve && pMem->z == 0);
+
+ assert(pMem->szMalloc == 0 ||
+        pMem->szMalloc == sqlDbMallocSize(pMem->db, pMem->zMalloc));
+ if (pMem->szMalloc < n) {
+   if (n < 32)
+     n = 32;
+   if (bPreserve && pMem->szMalloc > 0 && pMem->z == pMem->zMalloc) {
+     pMem->z = pMem->zMalloc =
+         sqlDbReallocOrFree(pMem->db, pMem->z, n);
+     bPreserve = 0;
+   } else {
+     if (pMem->szMalloc > 0)
+       sqlDbFree(pMem->db, pMem->zMalloc);
+     pMem->zMalloc = sqlDbMallocRaw(pMem->db, n);
+   }
+   if (pMem->zMalloc == 0) {
+     sqlVdbeMemSetNull(pMem);
+     pMem->z = 0;
+     pMem->szMalloc = 0;
+     return -1;
+   } else {
+     pMem->szMalloc = sqlDbMallocSize(pMem->db,
+              pMem->zMalloc);
+   }
+ }
+
+ if (bPreserve && pMem->z && pMem->z != pMem->zMalloc) {
+   memcpy(pMem->zMalloc, pMem->z, pMem->n);
+ }
+ if ((pMem->flags & MEM_Dyn) != 0) {
+   assert(pMem->xDel != 0 && pMem->xDel != SQL_DYNAMIC);
+   pMem->xDel((void *)(pMem->z));
+ }
+
+ pMem->z = pMem->zMalloc;
+ pMem->flags &= ~(MEM_Dyn | MEM_Ephem | MEM_Static);
+ return 0;
+}
+
+/*
+ * Change the pMem->zMalloc allocation to be at least szNew bytes.
+ * If pMem->zMalloc already meets or exceeds the requested size, this
+ * routine is a no-op.
+ *
+ * Any prior string or blob content in the pMem object may be discarded.
+ * The pMem->xDel destructor is called, if it exists.  Though MEM_Str
+ * and MEM_Blob values may be discarded, MEM_Int, MEM_Real, and MEM_Null
+ * values are preserved.
+ *
+ * Return 0 on success or -1 if unable to complete the resizing.
+ */
+int
+sqlVdbeMemClearAndResize(Mem * pMem, int szNew)
+{
+ assert(szNew > 0);
+ assert((pMem->flags & MEM_Dyn) == 0 || pMem->szMalloc == 0);
+ if (pMem->szMalloc < szNew) {
+   return sqlVdbeMemGrow(pMem, szNew, 0);
+ }
+ assert((pMem->flags & MEM_Dyn) == 0);
+ pMem->z = pMem->zMalloc;
+ pMem->flags &= (MEM_Null | MEM_Int | MEM_Real);
+ return 0;
+}
+
+void
+mem_set_bool(struct Mem *mem, bool value)
+{
+ sqlVdbeMemSetNull(mem);
+ mem->u.b = value;
+ mem->flags = MEM_Bool;
+ mem->field_type = FIELD_TYPE_BOOLEAN;
+}
+
+void
+mem_set_ptr(struct Mem *mem, void *ptr)
+{
+ sqlVdbeMemRelease(mem);
+ mem->flags = MEM_Ptr;
+ mem->u.p = ptr;
+}
+
+void
+mem_set_i64(struct Mem *mem, int64_t value)
+{
+ if (VdbeMemDynamic(mem))
+   sqlVdbeMemSetNull(mem);
+ mem->u.i = value;
+ int flag = value < 0 ? MEM_Int : MEM_UInt;
+ MemSetTypeFlag(mem, flag);
+ mem->field_type = FIELD_TYPE_INTEGER;
+}
+
+void
+mem_set_u64(struct Mem *mem, uint64_t value)
+{
+ if (VdbeMemDynamic(mem))
+   sqlVdbeMemSetNull(mem);
+ mem->u.u = value;
+ MemSetTypeFlag(mem, MEM_UInt);
+ mem->field_type = FIELD_TYPE_UNSIGNED;
+}
+
+void
+mem_set_int(struct Mem *mem, int64_t value, bool is_neg)
+{
+ if (VdbeMemDynamic(mem))
+   sqlVdbeMemSetNull(mem);
+ if (is_neg) {
+   assert(value < 0);
+   mem->u.i = value;
+   MemSetTypeFlag(mem, MEM_Int);
+ } else {
+   mem->u.u = value;
+   MemSetTypeFlag(mem, MEM_UInt);
+ }
+ mem->field_type = FIELD_TYPE_INTEGER;
+}
+
+void
+mem_set_double(struct Mem *mem, double value)
+{
+ sqlVdbeMemSetNull(mem);
+ if (sqlIsNaN(value))
+   return;
+ mem->u.r = value;
+ MemSetTypeFlag(mem, MEM_Real);
+ mem->field_type = FIELD_TYPE_DOUBLE;
+}
+
+/*
+ * Change the value of a Mem to be a string or a BLOB.
+ *
+ * The memory management strategy depends on the value of the xDel
+ * parameter. If the value passed is SQL_TRANSIENT, then the
+ * string is copied into a (possibly existing) buffer managed by the
+ * Mem structure. Otherwise, any existing buffer is freed and the
+ * pointer copied.
+ *
+ * If the string is too large (if it exceeds the SQL_LIMIT_LENGTH
+ * size limit) then no memory allocation occurs.  If the string can be
+ * stored without allocating memory, then it is.  If a memory allocation
+ * is required to store the string, then value of pMem is unchanged.  In
+ * either case, error is returned.
+ */
+int
+sqlVdbeMemSetStr(Mem * pMem, /* Memory cell to set to string value */
+        const char *z, /* String pointer */
+        int n, /* Bytes in string, or negative */
+        u8 not_blob, /* Encoding of z.  0 for BLOBs */
+        void (*xDel) (void *)  /* Destructor function */
+    )
+{
+ int nByte = n;    /* New value for pMem->n */
+ int iLimit;   /* Maximum allowed string or blob size */
+ u16 flags = 0;    /* New value for pMem->flags */
+
+ /* If z is a NULL pointer, set pMem to contain an SQL NULL. */
+ if (!z) {
+   sqlVdbeMemSetNull(pMem);
+   return 0;
+ }
+
+ if (pMem->db) {
+   iLimit = pMem->db->aLimit[SQL_LIMIT_LENGTH];
+ } else {
+   iLimit = SQL_MAX_LENGTH;
+ }
+ flags = (not_blob == 0 ? MEM_Blob : MEM_Str);
+ if (nByte < 0) {
+   assert(not_blob != 0);
+   nByte = sqlStrlen30(z);
+   if (nByte > iLimit)
+     nByte = iLimit + 1;
+   flags |= MEM_Term;
+ }
+
+ /* The following block sets the new values of Mem.z and Mem.xDel. It
+  * also sets a flag in local variable "flags" to indicate the memory
+  * management (one of MEM_Dyn or MEM_Static).
+  */
+ if (xDel == SQL_TRANSIENT) {
+   int nAlloc = nByte;
+   if (flags & MEM_Term) {
+     nAlloc += 1; //SQL_UTF8
+   }
+   if (nByte > iLimit) {
+     diag_set(ClientError, ER_SQL_EXECUTE, "string or binary"\
+        "string is too big");
+     return -1;
+   }
+   testcase(nAlloc == 0);
+   testcase(nAlloc == 31);
+   testcase(nAlloc == 32);
+   if (sqlVdbeMemClearAndResize(pMem, MAX(nAlloc, 32))) {
+     return -1;
+   }
+   memcpy(pMem->z, z, nAlloc);
+ } else if (xDel == SQL_DYNAMIC) {
+   sqlVdbeMemRelease(pMem);
+   pMem->zMalloc = pMem->z = (char *)z;
+   pMem->szMalloc = sqlDbMallocSize(pMem->db, pMem->zMalloc);
+ } else {
+   sqlVdbeMemRelease(pMem);
+   pMem->z = (char *)z;
+   pMem->xDel = xDel;
+   flags |= ((xDel == SQL_STATIC) ? MEM_Static : MEM_Dyn);
+ }
+
+ pMem->n = nByte;
+ pMem->flags = flags;
+ assert((pMem->flags & (MEM_Str | MEM_Blob)) != 0);
+ if ((pMem->flags & MEM_Str) != 0)
+   pMem->field_type = FIELD_TYPE_STRING;
+ else
+   pMem->field_type = FIELD_TYPE_VARBINARY;
+
+ if (nByte > iLimit) {
+   diag_set(ClientError, ER_SQL_EXECUTE, "string or binary string"\
+      "is too big");
+   return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize bulk memory to be a consistent Mem object.
+ *
+ * The minimum amount of initialization feasible is performed.
+ */
+void
+sqlVdbeMemInit(Mem * pMem, sql * db, u32 flags)
+{
+ assert((flags & ~MEM_TypeMask) == 0);
+ pMem->flags = flags;
+ pMem->db = db;
+ pMem->szMalloc = 0;
+ pMem->field_type = field_type_MAX;
+}
+
+/*
+ * Delete any previous value and set the value stored in *pMem to NULL.
+ *
+ * This routine calls the Mem.xDel destructor to dispose of values that
+ * require the destructor.  But it preserves the Mem.zMalloc memory allocation.
+ * To free all resources, use sqlVdbeMemRelease(), which both calls this
+ * routine to invoke the destructor and deallocates Mem.zMalloc.
+ *
+ * Use this routine to reset the Mem prior to insert a new value.
+ *
+ * Use sqlVdbeMemRelease() to complete erase the Mem prior to abandoning it.
+ */
+void
+sqlVdbeMemSetNull(Mem * pMem)
+{
+ if (VdbeMemDynamic(pMem)) {
+   vdbeMemClearExternAndSetNull(pMem);
+ } else {
+   pMem->flags = MEM_Null;
+ }
+}
+
+/*
+ * Delete any previous value and set the value to be a BLOB of length
+ * n containing all zeros.
+ */
+void
+sqlVdbeMemSetZeroBlob(Mem * pMem, int n)
+{
+ sqlVdbeMemRelease(pMem);
+ pMem->flags = MEM_Blob | MEM_Zero;
+ pMem->n = 0;
+ if (n < 0)
+   n = 0;
+ pMem->u.nZero = n;
+ pMem->z = 0;
+}
+
+/*
+ * Change the string value of an sql_value object
+ */
+void
+sqlValueSetStr(sql_value * v,  /* Value to be set */
+      int n, /* Length of string z */
+      const void *z, /* Text of the new string */
+      void (*xDel) (void *)  /* Destructor for the string */
+    )
+{
+ if (v)
+   sqlVdbeMemSetStr((Mem *) v, z, n, 1, xDel);
+}
+
+void
+sqlValueSetNull(sql_value * p)
+{
+ sqlVdbeMemSetNull((Mem *) p);
+}
+
+/*
+ * Free an sql_value object
+ */
+void
+sqlValueFree(sql_value * v)
+{
+ if (!v)
+   return;
+ sqlVdbeMemRelease((Mem *) v);
+ sqlDbFree(((Mem *) v)->db, v);
+}
+
+/*
+ * Create a new sql_value object.
+ */
+sql_value *
+sqlValueNew(sql * db)
+{
+ Mem *p = sqlDbMallocZero(db, sizeof(*p));
+ if (p) {
+   p->flags = MEM_Null;
+   p->db = db;
+ }
+ return p;
+}
+
+void
+initMemArray(Mem * p, int N, sql * db, u32 flags)
+{
+ while ((N--) > 0) {
+   p->db = db;
+   p->flags = flags;
+   p->szMalloc = 0;
+   p->field_type = field_type_MAX;
+#ifdef SQL_DEBUG
+   p->pScopyFrom = 0;
+#endif
+   p++;
+ }
+}
+
+void
+releaseMemArray(Mem * p, int N)
+{
+ if (p && N) {
+   Mem *pEnd = &p[N];
+   sql *db = p->db;
+   do {
+     assert((&p[1]) == pEnd || p[0].db == p[1].db);
+     assert(sqlVdbeCheckMemInvariants(p));
+
+     /* This block is really an inlined version of sqlVdbeMemRelease()
+      * that takes advantage of the fact that the memory cell value is
+      * being set to NULL after releasing any dynamic resources.
+      *
+      * The justification for duplicating code is that according to
+      * callgrind, this causes a certain test case to hit the CPU 4.7
+      * percent less (x86 linux, gcc version 4.1.2, -O6) than if
+      * sqlMemRelease() were called from here. With -O2, this jumps
+      * to 6.6 percent. The test case is inserting 1000 rows into a table
+      * with no indexes using a single prepared INSERT statement, bind()
+      * and reset(). Inserts are grouped into a transaction.
+      */
+     testcase(p->flags & MEM_Agg);
+     testcase(p->flags & MEM_Dyn);
+     testcase(p->flags & MEM_Frame);
+     if (p->
+         flags & (MEM_Agg | MEM_Dyn | MEM_Frame)) {
+       sqlVdbeMemRelease(p);
+     } else if (p->szMalloc) {
+       sqlDbFree(db, p->zMalloc);
+       p->szMalloc = 0;
+     }
+
+     p->flags = MEM_Undefined;
+   } while ((++p) < pEnd);
+ }
+}
+
+int
+mem_value_bool(const struct Mem *mem, bool *b)
+{
+ if ((mem->flags  & MEM_Bool) != 0) {
+   *b = mem->u.b;
+   return 0;
+ }
+ return -1;
+}
+
+/*
+ * Return some kind of integer value which is the best we can do
+ * at representing the value that *pMem describes as an integer.
+ * If pMem is an integer, then the value is exact.  If pMem is
+ * a floating-point then the value returned is the integer part.
+ * If pMem is a string or blob, then we make an attempt to convert
+ * it into an integer and return that.  If pMem represents an
+ * an SQL-NULL value, return 0.
+ *
+ * If pMem represents a string value, its encoding might be changed.
+ */
+int
+sqlVdbeIntValue(Mem * pMem, int64_t *i, bool *is_neg)
+{
+ int flags;
+ assert(EIGHT_BYTE_ALIGNMENT(pMem));
+ flags = pMem->flags;
+ if (flags & MEM_Int) {
+   *i = pMem->u.i;
+   *is_neg = true;
+   return 0;
+ } else if (flags & MEM_UInt) {
+   *i = pMem->u.u;
+   *is_neg = false;
+   return 0;
+ } else if (flags & MEM_Real) {
+   *is_neg = pMem->u.r < 0;
+   return doubleToInt64(pMem->u.r, i);
+ } else if (flags & (MEM_Str)) {
+   assert(pMem->z || pMem->n == 0);
+   if (sql_atoi64(pMem->z, i, is_neg, pMem->n) == 0)
+     return 0;
+ }
+ return -1;
+}
+
+/*
+ * Return the best representation of pMem that we can get into a
+ * double.  If pMem is already a double or an integer, return its
+ * value.  If it is a string or blob, try to convert it to a double.
+ * If it is a NULL, return 0.0.
+ */
+int
+sqlVdbeRealValue(Mem * pMem, double *v)
+{
+ assert(EIGHT_BYTE_ALIGNMENT(pMem));
+ if (pMem->flags & MEM_Real) {
+   *v = pMem->u.r;
+   return 0;
+ } else if (pMem->flags & MEM_Int) {
+   *v = (double)pMem->u.i;
+   return 0;
+ } else if ((pMem->flags & MEM_UInt) != 0) {
+   *v = (double)pMem->u.u;
+   return 0;
+ } else if (pMem->flags & MEM_Str) {
+   if (sqlAtoF(pMem->z, v, pMem->n))
+     return 0;
+ }
+ return -1;
+}
+
+/**************************** sql_value_  ******************************
+ * The following routines extract information from a Mem or sql_value
+ * structure.
+ */
+const void *
+sql_value_blob(sql_value * pVal)
+{
+ Mem *p = (Mem *) pVal;
+ if (p->flags & (MEM_Blob | MEM_Str)) {
+   if (ExpandBlob(p) != 0) {
+     assert(p->flags == MEM_Null && p->z == 0);
+     return 0;
+   }
+   p->flags |= MEM_Blob;
+   return p->n ? p->z : 0;
+ } else {
+   return sql_value_text(pVal);
+ }
+}
+
+int
+sql_value_bytes(sql_value * pVal)
+{
+ return sqlValueBytes(pVal);
+}
+
+double
+sql_value_double(sql_value * pVal)
+{
+ double v = 0.0;
+ sqlVdbeRealValue((Mem *) pVal, &v);
+ return v;
+}
+
+bool
+sql_value_boolean(sql_value *val)
+{
+ bool b = false;
+ int rc = mem_value_bool((struct Mem *) val, &b);
+ assert(rc == 0);
+ (void) rc;
+ return b;
+}
+
+int
+sql_value_int(sql_value * pVal)
+{
+ int64_t i = 0;
+ bool is_neg;
+ sqlVdbeIntValue((Mem *) pVal, &i, &is_neg);
+ return (int)i;
+}
+
+sql_int64
+sql_value_int64(sql_value * pVal)
+{
+ int64_t i = 0;
+ bool unused;
+ sqlVdbeIntValue((Mem *) pVal, &i, &unused);
+ return i;
+}
+
+uint64_t
+sql_value_uint64(sql_value *val)
+{
+ int64_t i = 0;
+ bool is_neg;
+ sqlVdbeIntValue((struct Mem *) val, &i, &is_neg);
+ assert(!is_neg);
+ return i;
+}
+
+const unsigned char *
+sql_value_text(sql_value * pVal)
+{
+ return (const unsigned char *)sqlValueText(pVal);
+}
+
+/* This function is only available internally, it is not part of the
+ * external API. It works in a similar way to sql_value_text(),
+ * except the data returned is in the encoding specified by the second
+ * parameter, which must be one of SQL_UTF16BE, SQL_UTF16LE or
+ * SQL_UTF8.
+ *
+ * (2006-02-16:)  The enc value can be or-ed with SQL_UTF16_ALIGNED.
+ * If that is the case, then the result must be aligned on an even byte
+ * boundary.
+ */
+const void *
+sqlValueText(sql_value * pVal)
+{
+ if (!pVal)
+   return 0;
+ if ((pVal->flags & (MEM_Str | MEM_Term)) == (MEM_Str | MEM_Term)) {
+   return pVal->z;
+ }
+ if (pVal->flags & MEM_Null) {
+   return 0;
+ }
+ return valueToText(pVal);
+}
+
+const char *
+sql_value_to_diag_str(sql_value *value)
+{
+ enum mp_type mp_type = sql_value_type(value);
+ if (mp_type_is_bloblike(mp_type)) {
+   if (mem_has_msgpack_subtype(value))
+     return sqlValueText(value);
+   return "varbinary";
+ }
+ return sqlValueText(value);
+}
+
+enum sql_subtype
+sql_value_subtype(sql_value * pVal)
+{
+ return (pVal->flags & MEM_Subtype) != 0 ? pVal->subtype : SQL_SUBTYPE_NO;
+}
+
+/*
+ * Return a pointer to static memory containing an SQL NULL value.
+ */
+const Mem *
+columnNullValue(void)
+{
+ /* Even though the Mem structure contains an element
+  * of type i64, on certain architectures (x86) with certain compiler
+  * switches (-Os), gcc may align this Mem object on a 4-byte boundary
+  * instead of an 8-byte one. This all works fine, except that when
+  * running with SQL_DEBUG defined the sql code sometimes assert()s
+  * that a Mem structure is located on an 8-byte boundary. To prevent
+  * these assert()s from failing, when building with SQL_DEBUG defined
+  * using gcc, we force nullMem to be 8-byte aligned using the magical
+  * __attribute__((aligned(8))) macro.
+  */
+ static const Mem nullMem
+#if defined(SQL_DEBUG) && defined(__GNUC__)
+     __attribute__ ((aligned(8)))
+#endif
+     = {
+   /* .u          = */  {
+   0},
+       /* .flags      = */ (u16) MEM_Null,
+       /* .eSubtype   = */ (u8) 0,
+       /* .field_type = */ field_type_MAX,
+       /* .n          = */ (int)0,
+       /* .z          = */ (char *)0,
+       /* .zMalloc    = */ (char *)0,
+       /* .szMalloc   = */ (int)0,
+       /* .uTemp      = */ (u32) 0,
+       /* .db         = */ (sql *) 0,
+       /* .xDel       = */ (void (*)(void *))0,
+#ifdef SQL_DEBUG
+       /* .pScopyFrom = */ (Mem *) 0,
+       /* .pFiller    = */ (void *)0,
+#endif
+ };
+ return &nullMem;
+}
+
+/*
+ * Return true if the Mem object contains a TEXT or BLOB that is
+ * too large - whose size exceeds SQL_MAX_LENGTH.
+ */
+int
+sqlVdbeMemTooBig(Mem * p)
+{
+ assert(p->db != 0);
+ if (p->flags & (MEM_Str | MEM_Blob)) {
+   int n = p->n;
+   if (p->flags & MEM_Zero) {
+     n += p->u.nZero;
+   }
+   return n > p->db->aLimit[SQL_LIMIT_LENGTH];
+ }
+ return 0;
+}
+
+/*
+ * Compare two blobs.  Return negative, zero, or positive if the first
+ * is less than, equal to, or greater than the second, respectively.
+ * If one blob is a prefix of the other, then the shorter is the lessor.
+ */
+static SQL_NOINLINE int
+sqlBlobCompare(const Mem * pB1, const Mem * pB2)
+{
+ int c;
+ int n1 = pB1->n;
+ int n2 = pB2->n;
+
+ /* It is possible to have a Blob value that has some non-zero content
+  * followed by zero content.  But that only comes up for Blobs formed
+  * by the OP_MakeRecord opcode, and such Blobs never get passed into
+  * sqlMemCompare().
+  */
+ assert((pB1->flags & MEM_Zero) == 0 || n1 == 0);
+ assert((pB2->flags & MEM_Zero) == 0 || n2 == 0);
+
+ if ((pB1->flags | pB2->flags) & MEM_Zero) {
+   if (pB1->flags & pB2->flags & MEM_Zero) {
+     return pB1->u.nZero - pB2->u.nZero;
+   } else if (pB1->flags & MEM_Zero) {
+     if (!isAllZero(pB2->z, pB2->n))
+       return -1;
+     return pB1->u.nZero - n2;
+   } else {
+     if (!isAllZero(pB1->z, pB1->n))
+       return +1;
+     return n1 - pB2->u.nZero;
+   }
+ }
+ c = memcmp(pB1->z, pB2->z, n1 > n2 ? n2 : n1);
+ if (c)
+   return c;
+ return n1 - n2;
+}
+
+/*
+ * Compare the values contained by the two memory cells, returning
+ * negative, zero or positive if pMem1 is less than, equal to, or greater
+ * than pMem2. Sorting order is NULL's first, followed by numbers (integers
+ * and reals) sorted numerically, followed by text ordered by the collating
+ * sequence pColl and finally blob's ordered by memcmp().
+ *
+ * Two NULL values are considered equal by this function.
+ */
+int
+sqlMemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pColl)
+{
+ int f1, f2;
+ int combined_flags;
+
+ f1 = pMem1->flags;
+ f2 = pMem2->flags;
+ combined_flags = f1 | f2;
+
+ /* If one value is NULL, it is less than the other. If both values
+  * are NULL, return 0.
+  */
+ if (combined_flags & MEM_Null) {
+   return (f2 & MEM_Null) - (f1 & MEM_Null);
+ }
+
+ if ((combined_flags & MEM_Bool) != 0) {
+   if ((f1 & f2 & MEM_Bool) != 0) {
+     if (pMem1->u.b == pMem2->u.b)
+       return 0;
+     if (pMem1->u.b)
+       return 1;
+     return -1;
+   }
+   if ((f2 & MEM_Bool) != 0)
+     return +1;
+   return -1;
+ }
+
+ /* At least one of the two values is a number
+  */
+ if ((combined_flags & (MEM_Int | MEM_UInt | MEM_Real)) != 0) {
+   if ((f1 & f2 & MEM_Int) != 0) {
+     if (pMem1->u.i < pMem2->u.i)
+       return -1;
+     if (pMem1->u.i > pMem2->u.i)
+       return +1;
+     return 0;
+   }
+   if ((f1 & f2 & MEM_UInt) != 0) {
+     if (pMem1->u.u < pMem2->u.u)
+       return -1;
+     if (pMem1->u.u > pMem2->u.u)
+       return +1;
+     return 0;
+   }
+   if ((f1 & f2 & MEM_Real) != 0) {
+     if (pMem1->u.r < pMem2->u.r)
+       return -1;
+     if (pMem1->u.r > pMem2->u.r)
+       return +1;
+     return 0;
+   }
+   if ((f1 & MEM_Int) != 0) {
+     if ((f2 & MEM_Real) != 0) {
+       return double_compare_nint64(pMem2->u.r,
+                  pMem1->u.i, -1);
+     } else {
+       return -1;
+     }
+   }
+   if ((f1 & MEM_UInt) != 0) {
+     if ((f2 & MEM_Real) != 0) {
+       return double_compare_uint64(pMem2->u.r,
+                  pMem1->u.u, -1);
+     } else if ((f2 & MEM_Int) != 0) {
+       return +1;
+     } else {
+       return -1;
+     }
+   }
+   if ((f1 & MEM_Real) != 0) {
+     if ((f2 & MEM_Int) != 0) {
+       return double_compare_nint64(pMem1->u.r,
+                  pMem2->u.i, 1);
+     } else if ((f2 & MEM_UInt) != 0) {
+       return double_compare_uint64(pMem1->u.r,
+                  pMem2->u.u, 1);
+     } else {
+       return -1;
+     }
+   }
+   return +1;
+ }
+
+ /* If one value is a string and the other is a blob, the string is less.
+  * If both are strings, compare using the collating functions.
+  */
+ if (combined_flags & MEM_Str) {
+   if ((f1 & MEM_Str) == 0) {
+     return 1;
+   }
+   if ((f2 & MEM_Str) == 0) {
+     return -1;
+   }
+   /* The collation sequence must be defined at this point, even if
+    * the user deletes the collation sequence after the vdbe program is
+    * compiled (this was not always the case).
+    */
+   if (pColl) {
+     return vdbeCompareMemString(pMem1, pMem2, pColl);
+   } else {
+     size_t n = pMem1->n < pMem2->n ? pMem1->n : pMem2->n;
+     int res;
+     res = memcmp(pMem1->z, pMem2->z, n);
+     if (res == 0)
+       res = (int)pMem1->n - (int)pMem2->n;
+     return res;
+   }
+   /* If a NULL pointer was passed as the collate function, fall through
+    * to the blob case and use memcmp().
+    */
+ }
+
+ /* Both values must be blobs.  Compare using memcmp().  */
+ return sqlBlobCompare(pMem1, pMem2);
+}
+
+bool
+mem_is_type_compatible(struct Mem *mem, enum field_type type)
+{
+ enum mp_type mp_type = mem_mp_type(mem);
+ assert(mp_type < MP_EXT);
+ return field_mp_plain_type_is_compatible(type, mp_type, true);
+}
+
+/* Allocate memory for internal VDBE structure on region. */
+int
+vdbe_mem_alloc_blob_region(struct Mem *vdbe_mem, uint32_t size)
+{
+ vdbe_mem->n = size;
+ vdbe_mem->z = region_alloc(&fiber()->gc, size);
+ if (vdbe_mem->z == NULL)
+   return -1;
+ vdbe_mem->flags = MEM_Ephem | MEM_Blob;
+ assert(sqlVdbeCheckMemInvariants(vdbe_mem));
+ return 0;
+}
+
+/*
+ * Make a full copy of pFrom into pTo.  Prior contents of pTo are
+ * freed before the copy is made.
+ */
+int
+sqlVdbeMemCopy(Mem * pTo, const Mem * pFrom)
+{
+ int rc = 0;
+
+ if (VdbeMemDynamic(pTo))
+   vdbeMemClearExternAndSetNull(pTo);
+ memcpy(pTo, pFrom, MEMCELLSIZE);
+ pTo->flags &= ~MEM_Dyn;
+ if (pTo->flags & (MEM_Str | MEM_Blob)) {
+   if (0 == (pFrom->flags & MEM_Static)) {
+     pTo->flags |= MEM_Ephem;
+     rc = sqlVdbeMemMakeWriteable(pTo);
+   }
+ }
+
+ return rc;
+}
+
+void
+sqlVdbeMemShallowCopy(Mem * pTo, const Mem * pFrom, int srcType)
+{
+ assert(pTo->db == pFrom->db);
+ if (VdbeMemDynamic(pTo)) {
+   vdbeClrCopy(pTo, pFrom, srcType);
+   return;
+ }
+ memcpy(pTo, pFrom, MEMCELLSIZE);
+ if ((pFrom->flags & MEM_Static) == 0) {
+   pTo->flags &= ~(MEM_Dyn | MEM_Static | MEM_Ephem);
+   assert(srcType == MEM_Ephem || srcType == MEM_Static);
+   pTo->flags |= srcType;
+ }
+}
+
+/*
+ * Transfer the contents of pFrom to pTo. Any existing value in pTo is
+ * freed. If pFrom contains ephemeral data, a copy is made.
+ *
+ * pFrom contains an SQL NULL when this routine returns.
+ */
+void
+sqlVdbeMemMove(Mem * pTo, Mem * pFrom)
+{
+ assert(pFrom->db == 0 || pTo->db == 0 || pFrom->db == pTo->db);
+
+ sqlVdbeMemRelease(pTo);
+ memcpy(pTo, pFrom, sizeof(Mem));
+ pFrom->flags = MEM_Null;
+ pFrom->szMalloc = 0;
+}
+
+/*
+ * Change pMem so that its MEM_Str or MEM_Blob value is stored in
+ * MEM.zMalloc, where it can be safely written.
+ *
+ * Return 0 on success or -1 if malloc fails.
+ */
+int
+sqlVdbeMemMakeWriteable(Mem * pMem)
+{
+ if ((pMem->flags & (MEM_Str | MEM_Blob)) != 0) {
+   if (ExpandBlob(pMem))
+     return -1;
+   if (pMem->szMalloc == 0 || pMem->z != pMem->zMalloc) {
+     if (sqlVdbeMemGrow(pMem, pMem->n + 2, 1)) {
+       return -1;
+     }
+     pMem->z[pMem->n] = 0;
+     pMem->z[pMem->n + 1] = 0;
+     pMem->flags |= MEM_Term;
+   }
+ }
+ pMem->flags &= ~MEM_Ephem;
+#ifdef SQL_DEBUG
+ pMem->pScopyFrom = 0;
+#endif
+
+ return 0;
+}
+
+/*
+ * Release any memory resources held by the Mem.  Both the memory that is
+ * free by Mem.xDel and the Mem.zMalloc allocation are freed.
+ *
+ * Use this routine prior to clean up prior to abandoning a Mem, or to
+ * reset a Mem back to its minimum memory utilization.
+ *
+ * Use sqlVdbeMemSetNull() to release just the Mem.xDel space
+ * prior to inserting new content into the Mem.
+ */
+void
+sqlVdbeMemRelease(Mem * p)
+{
+ assert(sqlVdbeCheckMemInvariants(p));
+ if (VdbeMemDynamic(p) || p->szMalloc) {
+   vdbeMemClear(p);
+ }
+}
+
+int
+sql_vdbemem_finalize(struct Mem *mem, struct func *func)
+{
+ assert(func != NULL);
+ assert(func->def->language == FUNC_LANGUAGE_SQL_BUILTIN);
+ assert(func->def->aggregate == FUNC_AGGREGATE_GROUP);
+ assert((mem->flags & MEM_Null) != 0 || func == mem->u.func);
+ sql_context ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ Mem t;
+ memset(&t, 0, sizeof(t));
+ t.flags = MEM_Null;
+ t.db = mem->db;
+ t.field_type = field_type_MAX;
+ ctx.pOut = &t;
+ ctx.pMem = mem;
+ ctx.func = func;
+ ((struct func_sql_builtin *)func)->finalize(&ctx);
+ assert((mem->flags & MEM_Dyn) == 0);
+ if (mem->szMalloc > 0)
+   sqlDbFree(mem->db, mem->zMalloc);
+ memcpy(mem, &t, sizeof(t));
+ return ctx.is_aborted ? -1 : 0;
+}
+
+int
+sqlVdbeCompareMsgpack(const char **key1,
+       struct UnpackedRecord *unpacked, int key2_idx)
+{
+ const char *aKey1 = *key1;
+ Mem *pKey2 = unpacked->aMem + key2_idx;
+ Mem mem1;
+ int rc = 0;
+ switch (mp_typeof(*aKey1)) {
+ default:{
+     /* FIXME */
+     rc = -1;
+     break;
+   }
+ case MP_NIL:{
+     rc = -((pKey2->flags & MEM_Null) == 0);
+     mp_decode_nil(&aKey1);
+     break;
+   }
+ case MP_BOOL:{
+     mem1.u.b = mp_decode_bool(&aKey1);
+     if ((pKey2->flags & MEM_Bool) != 0) {
+       if (mem1.u.b != pKey2->u.b)
+         rc = mem1.u.b ? 1 : -1;
+     } else {
+       rc = (pKey2->flags & MEM_Null) != 0 ? 1 : -1;
+     }
+     break;
+   }
+ case MP_UINT:{
+     mem1.u.u = mp_decode_uint(&aKey1);
+     if ((pKey2->flags & MEM_Int) != 0) {
+       rc = +1;
+     } else if ((pKey2->flags & MEM_UInt) != 0) {
+       if (mem1.u.u < pKey2->u.u)
+         rc = -1;
+       else if (mem1.u.u > pKey2->u.u)
+         rc = +1;
+     } else if ((pKey2->flags & MEM_Real) != 0) {
+       rc = double_compare_uint64(pKey2->u.r,
+                mem1.u.u, -1);
+     } else if ((pKey2->flags & MEM_Null) != 0) {
+       rc = 1;
+     } else if ((pKey2->flags & MEM_Bool) != 0) {
+       rc = 1;
+     } else {
+       rc = -1;
+     }
+     break;
+   }
+ case MP_INT:{
+     mem1.u.i = mp_decode_int(&aKey1);
+     if ((pKey2->flags & MEM_UInt) != 0) {
+       rc = -1;
+     } else if ((pKey2->flags & MEM_Int) != 0) {
+       if (mem1.u.i < pKey2->u.i) {
+         rc = -1;
+       } else if (mem1.u.i > pKey2->u.i) {
+         rc = +1;
+       }
+     } else if (pKey2->flags & MEM_Real) {
+       rc = double_compare_nint64(pKey2->u.r, mem1.u.i,
+                -1);
+     } else if ((pKey2->flags & MEM_Null) != 0) {
+       rc = 1;
+     } else if ((pKey2->flags & MEM_Bool) != 0) {
+       rc = 1;
+     } else {
+       rc = -1;
+     }
+     break;
+   }
+ case MP_FLOAT:{
+     mem1.u.r = mp_decode_float(&aKey1);
+     goto do_float;
+   }
+ case MP_DOUBLE:{
+     mem1.u.r = mp_decode_double(&aKey1);
+ do_float:
+     if ((pKey2->flags & MEM_Int) != 0) {
+       rc = double_compare_nint64(mem1.u.r, pKey2->u.i,
+                1);
+     } else if (pKey2->flags & MEM_UInt) {
+       rc = double_compare_uint64(mem1.u.r,
+                pKey2->u.u, 1);
+     } else if (pKey2->flags & MEM_Real) {
+       if (mem1.u.r < pKey2->u.r) {
+         rc = -1;
+       } else if (mem1.u.r > pKey2->u.r) {
+         rc = +1;
+       }
+     } else if ((pKey2->flags & MEM_Null) != 0) {
+       rc = 1;
+     } else if ((pKey2->flags & MEM_Bool) != 0) {
+       rc = 1;
+     } else {
+       rc = -1;
+     }
+     break;
+   }
+ case MP_STR:{
+     if (pKey2->flags & MEM_Str) {
+       struct key_def *key_def = unpacked->key_def;
+       mem1.n = mp_decode_strl(&aKey1);
+       mem1.z = (char *)aKey1;
+       aKey1 += mem1.n;
+       struct coll *coll =
+         key_def->parts[key2_idx].coll;
+       if (coll != NULL) {
+         mem1.flags = MEM_Str;
+         rc = vdbeCompareMemString(&mem1, pKey2,
+                 coll);
+       } else {
+         goto do_bin_cmp;
+       }
+     } else {
+       rc = (pKey2->flags & MEM_Blob) ? -1 : +1;
+     }
+     break;
+   }
+ case MP_BIN:{
+     mem1.n = mp_decode_binl(&aKey1);
+     mem1.z = (char *)aKey1;
+     aKey1 += mem1.n;
+ do_blob:
+     if (pKey2->flags & MEM_Blob) {
+       if (pKey2->flags & MEM_Zero) {
+         if (!isAllZero
+             ((const char *)mem1.z, mem1.n)) {
+           rc = 1;
+         } else {
+           rc = mem1.n - pKey2->u.nZero;
+         }
+       } else {
+         int nCmp;
+ do_bin_cmp:
+         nCmp = MIN(mem1.n, pKey2->n);
+         rc = memcmp(mem1.z, pKey2->z, nCmp);
+         if (rc == 0)
+           rc = mem1.n - pKey2->n;
+       }
+     } else {
+       rc = 1;
+     }
+     break;
+   }
+ case MP_ARRAY:
+ case MP_MAP:
+ case MP_EXT:{
+     mem1.z = (char *)aKey1;
+     mp_next(&aKey1);
+     mem1.n = aKey1 - (char *)mem1.z;
+     goto do_blob;
+   }
+ }
+ *key1 = aKey1;
+ return rc;
+}
+
+int
+sqlVdbeRecordCompareMsgpack(const void *key1,
+       struct UnpackedRecord *key2)
+{
+ int rc = 0;
+ u32 i, n = mp_decode_array((const char**)&key1);
+
+ n = MIN(n, key2->nField);
+
+ for (i = 0; i != n; i++) {
+   rc = sqlVdbeCompareMsgpack((const char**)&key1, key2, i);
+   if (rc != 0) {
+     if (key2->key_def->parts[i].sort_order !=
+         SORT_ORDER_ASC) {
+       rc = -rc;
+     }
+     return rc;
+   }
+ }
+
+ key2->eqSeen = 1;
+ return key2->default_rc;
+}
+
+int
+vdbe_decode_msgpack_into_mem(const char *buf, struct Mem *mem, uint32_t *len)
+{
+ const char *start_buf = buf;
+ switch (mp_typeof(*buf)) {
+ case MP_ARRAY: {
+   mem->z = (char *)buf;
+   mp_next(&buf);
+   mem->n = buf - mem->z;
+   mem->flags = MEM_Blob | MEM_Ephem | MEM_Subtype;
+   mem->subtype = SQL_SUBTYPE_MSGPACK;
+   mem->field_type = FIELD_TYPE_ARRAY;
+   break;
+ }
+ case MP_MAP: {
+   mem->z = (char *)buf;
+   mp_next(&buf);
+   mem->n = buf - mem->z;
+   mem->flags = MEM_Blob | MEM_Ephem | MEM_Subtype;
+   mem->subtype = SQL_SUBTYPE_MSGPACK;
+   mem->field_type = FIELD_TYPE_MAP;
+   break;
+ }
+ case MP_EXT: {
+   mem->z = (char *)buf;
+   mp_next(&buf);
+   mem->n = buf - mem->z;
+   mem->flags = MEM_Blob | MEM_Ephem;
+   mem->field_type = FIELD_TYPE_VARBINARY;
+   break;
+ }
+ case MP_NIL: {
+   mp_decode_nil(&buf);
+   mem->flags = MEM_Null;
+   mem->field_type = field_type_MAX;
+   break;
+ }
+ case MP_BOOL: {
+   mem->u.b = mp_decode_bool(&buf);
+   mem->flags = MEM_Bool;
+   mem->field_type = FIELD_TYPE_BOOLEAN;
+   break;
+ }
+ case MP_UINT: {
+   uint64_t v = mp_decode_uint(&buf);
+   mem->u.u = v;
+   mem->flags = MEM_UInt;
+   mem->field_type = FIELD_TYPE_INTEGER;
+   break;
+ }
+ case MP_INT: {
+   mem->u.i = mp_decode_int(&buf);
+   mem->flags = MEM_Int;
+   mem->field_type = FIELD_TYPE_INTEGER;
+   break;
+ }
+ case MP_STR: {
+   /* XXX u32->int */
+   mem->n = (int) mp_decode_strl(&buf);
+   mem->flags = MEM_Str | MEM_Ephem;
+   mem->field_type = FIELD_TYPE_STRING;
+install_blob:
+   mem->z = (char *)buf;
+   buf += mem->n;
+   break;
+ }
+ case MP_BIN: {
+   /* XXX u32->int */
+   mem->n = (int) mp_decode_binl(&buf);
+   mem->flags = MEM_Blob | MEM_Ephem;
+   mem->field_type = FIELD_TYPE_VARBINARY;
+   goto install_blob;
+ }
+ case MP_FLOAT: {
+   mem->u.r = mp_decode_float(&buf);
+   if (sqlIsNaN(mem->u.r)) {
+     mem->flags = MEM_Null;
+     mem->field_type = FIELD_TYPE_DOUBLE;
+   } else {
+     mem->flags = MEM_Real;
+     mem->field_type = FIELD_TYPE_DOUBLE;
+   }
+   break;
+ }
+ case MP_DOUBLE: {
+   mem->u.r = mp_decode_double(&buf);
+   if (sqlIsNaN(mem->u.r)) {
+     mem->flags = MEM_Null;
+     mem->field_type = FIELD_TYPE_DOUBLE;
+   } else {
+     mem->flags = MEM_Real;
+     mem->field_type = FIELD_TYPE_DOUBLE;
+   }
+   break;
+ }
+ default:
+   unreachable();
+ }
+ *len = (uint32_t)(buf - start_buf);
+ return 0;
+}
+
+void
+mpstream_encode_vdbe_mem(struct mpstream *stream, struct Mem *var)
+{
+ assert(memIsValid(var));
+ int64_t i;
+ if (var->flags & MEM_Null) {
+   mpstream_encode_nil(stream);
+ } else if (var->flags & MEM_Real) {
+   mpstream_encode_double(stream, var->u.r);
+ } else if (var->flags & MEM_Int) {
+   i = var->u.i;
+   mpstream_encode_int(stream, i);
+ } else if (var->flags & MEM_UInt) {
+   i = var->u.u;
+   mpstream_encode_uint(stream, i);
+ } else if (var->flags & MEM_Str) {
+   mpstream_encode_strn(stream, var->z, var->n);
+ } else if (var->flags & MEM_Bool) {
+   mpstream_encode_bool(stream, var->u.b);
+ } else {
+   /*
+    * Emit BIN header iff the BLOB doesn't store
+    * MsgPack content.
+    */
+   if (!mem_has_msgpack_subtype(var)) {
+     uint32_t binl = var->n +
+         ((var->flags & MEM_Zero) ?
+         var->u.nZero : 0);
+     mpstream_encode_binl(stream, binl);
+   }
+   mpstream_memcpy(stream, var->z, var->n);
+   if (var->flags & MEM_Zero)
+     mpstream_memset(stream, 0, var->u.nZero);
+ }
+}
+
+char *
+sql_vdbe_mem_encode_tuple(struct Mem *fields, uint32_t field_count,
+       uint32_t *tuple_size, struct region *region)
+{
+ size_t used = region_used(region);
+ bool is_error = false;
+ struct mpstream stream;
+ mpstream_init(&stream, region, region_reserve_cb, region_alloc_cb,
+         set_encode_error, &is_error);
+ mpstream_encode_array(&stream, field_count);
+ for (struct Mem *field = fields; field < fields + field_count; field++)
+   mpstream_encode_vdbe_mem(&stream, field);
+ mpstream_flush(&stream);
+ if (is_error) {
+   diag_set(OutOfMemory, stream.pos - stream.buf,
+      "mpstream_flush", "stream");
+   return NULL;
+ }
+ *tuple_size = region_used(region) - used;
+ char *tuple = region_join(region, *tuple_size);
+ if (tuple == NULL) {
+   diag_set(OutOfMemory, *tuple_size, "region_join", "tuple");
+   return NULL;
+ }
+ mp_tuple_assert(tuple, tuple + *tuple_size);
+ return tuple;
+}
+
+/**
+ * Allocate a sequence of initialized vdbe memory registers
+ * on region.
+ */
+static struct Mem *
+vdbemem_alloc_on_region(uint32_t count)
+{
+ struct region *region = &fiber()->gc;
+ size_t size;
+ struct Mem *ret = region_alloc_array(region, typeof(*ret), count,
+              &size);
+ if (ret == NULL) {
+   diag_set(OutOfMemory, size, "region_alloc_array", "ret");
+   return NULL;
+ }
+ memset(ret, 0, count * sizeof(*ret));
+ for (uint32_t i = 0; i < count; i++) {
+   sqlVdbeMemInit(&ret[i], sql_get(), MEM_Null);
+   assert(memIsValid(&ret[i]));
+ }
+ return ret;
+}
+
+static void
+port_vdbemem_dump_lua(struct port *base, struct lua_State *L, bool is_flat)
+{
+ (void) is_flat;
+ struct port_vdbemem *port = (struct port_vdbemem *) base;
+ assert(is_flat == true);
+ for (uint32_t i = 0; i < port->mem_count; i++) {
+   sql_value *param =
+     (sql_value *)((struct Mem *)port->mem + i);
+   switch (sql_value_type(param)) {
+   case MP_INT:
+     luaL_pushint64(L, sql_value_int64(param));
+     break;
+   case MP_UINT:
+     luaL_pushuint64(L, sql_value_uint64(param));
+     break;
+   case MP_DOUBLE:
+     lua_pushnumber(L, sql_value_double(param));
+     break;
+   case MP_STR:
+     lua_pushlstring(L, (const char *)sql_value_text(param),
+         (size_t)sql_value_bytes(param));
+     break;
+   case MP_BIN:
+   case MP_ARRAY:
+   case MP_MAP:
+     lua_pushlstring(L, sql_value_blob(param),
+         (size_t) sql_value_bytes(param));
+     break;
+   case MP_NIL:
+     lua_pushnil(L);
+     break;
+   case MP_BOOL:
+     lua_pushboolean(L, sql_value_boolean(param));
+     break;
+   default:
+     unreachable();
+   }
+ }
+}
+
+static const char *
+port_vdbemem_get_msgpack(struct port *base, uint32_t *size)
+{
+ struct port_vdbemem *port = (struct port_vdbemem *) base;
+ struct region *region = &fiber()->gc;
+ size_t region_svp = region_used(region);
+ bool is_error = false;
+ struct mpstream stream;
+ mpstream_init(&stream, region, region_reserve_cb, region_alloc_cb,
+         set_encode_error, &is_error);
+ mpstream_encode_array(&stream, port->mem_count);
+ for (uint32_t i = 0; i < port->mem_count && !is_error; i++) {
+   sql_value *param =
+     (sql_value *)((struct Mem *)port->mem + i);
+   switch (sql_value_type(param)) {
+   case MP_INT: {
+     sql_int64 val = sql_value_int64(param);
+     if (val < 0) {
+       mpstream_encode_int(&stream, val);
+       break;
+     }
+     FALLTHROUGH;
+   }
+   case MP_UINT: {
+     sql_uint64 val = sql_value_uint64(param);
+     mpstream_encode_uint(&stream, val);
+     break;
+   }
+   case MP_DOUBLE: {
+     mpstream_encode_double(&stream,
+                sql_value_double(param));
+     break;
+   }
+   case MP_STR: {
+     const char *str = (const char *) sql_value_text(param);
+     mpstream_encode_strn(&stream, str,
+              sql_value_bytes(param));
+     break;
+   }
+   case MP_BIN:
+   case MP_ARRAY:
+   case MP_MAP: {
+     mpstream_encode_binl(&stream, sql_value_bytes(param));
+     mpstream_memcpy(&stream, sql_value_blob(param),
+         sql_value_bytes(param));
+     break;
+   }
+   case MP_NIL: {
+     mpstream_encode_nil(&stream);
+     break;
+   }
+   case MP_BOOL: {
+     mpstream_encode_bool(&stream, sql_value_boolean(param));
+     break;
+   }
+   default:
+     unreachable();
+   }
+ }
+ mpstream_flush(&stream);
+ *size = region_used(region) - region_svp;
+ if (is_error)
+   goto error;
+ const char *ret = (char *)region_join(region, *size);
+ if (ret == NULL)
+   goto error;
+ return ret;
+error:
+ diag_set(OutOfMemory, *size, "region", "ret");
+ return NULL;
+}
+
+static const struct port_vtab port_vdbemem_vtab;
+
+void
+port_vdbemem_create(struct port *base, struct sql_value *mem,
+       uint32_t mem_count)
+{
+ struct port_vdbemem *port = (struct port_vdbemem *) base;
+ port->vtab = &port_vdbemem_vtab;
+ port->mem = mem;
+ port->mem_count = mem_count;
+}
+
+static struct sql_value *
+port_vdbemem_get_vdbemem(struct port *base, uint32_t *mem_count)
+{
+ struct port_vdbemem *port = (struct port_vdbemem *) base;
+ assert(port->vtab == &port_vdbemem_vtab);
+ *mem_count = port->mem_count;
+ return port->mem;
+}
+
+static const struct port_vtab port_vdbemem_vtab = {
+ .dump_msgpack = NULL,
+ .dump_msgpack_16 = NULL,
+ .dump_lua = port_vdbemem_dump_lua,
+ .dump_plain = NULL,
+ .get_msgpack = port_vdbemem_get_msgpack,
+ .get_vdbemem = port_vdbemem_get_vdbemem,
+ .destroy = NULL,
+};
+
+struct sql_value *
+port_lua_get_vdbemem(struct port *base, uint32_t *size)
+{
+ struct port_lua *port = (struct port_lua *) base;
+ struct lua_State *L = port->L;
+ int argc = lua_gettop(L);
+ if (argc == 0 || argc > 1) {
+   diag_set(ClientError, ER_SQL_FUNC_WRONG_RET_COUNT, "Lua", argc);
+   return NULL;
+ }
+ *size = argc;
+ /** FIXME: Implement an ability to return a vector. */
+ assert(*size == 1);
+ struct region *region = &fiber()->gc;
+ size_t region_svp = region_used(region);
+ struct Mem *val = vdbemem_alloc_on_region(argc);
+ if (val == NULL)
+   return NULL;
+ for (int i = 0; i < argc; i++) {
+   struct luaL_field field;
+   if (luaL_tofield(L, luaL_msgpack_default,
+        NULL, -1 - i, &field) < 0) {
+     goto error;
+   }
+   switch (field.type) {
+   case MP_BOOL:
+     mem_set_bool(&val[i], field.bval);
+     break;
+   case MP_FLOAT:
+     mem_set_double(&val[i], field.fval);
+     break;
+   case MP_DOUBLE:
+     mem_set_double(&val[i], field.dval);
+     break;
+   case MP_INT:
+     mem_set_i64(&val[i], field.ival);
+     break;
+   case MP_UINT:
+     mem_set_u64(&val[i], field.ival);
+     break;
+   case MP_STR:
+     if (sqlVdbeMemSetStr(&val[i], field.sval.data,
+              field.sval.len, 1,
+              SQL_TRANSIENT) != 0)
+       goto error;
+     break;
+   case MP_NIL:
+     sqlVdbeMemSetNull(&val[i]);
+     break;
+   default:
+     diag_set(ClientError, ER_SQL_EXECUTE,
+        "Unsupported type passed from Lua");
+     goto error;
+   }
+ }
+ return (struct sql_value *)val;
+error:
+ for (int i = 0; i < argc; i++)
+   sqlVdbeMemRelease(&val[i]);
+ region_truncate(region, region_svp);
+ return NULL;
+}
+
+struct sql_value *
+port_c_get_vdbemem(struct port *base, uint32_t *size)
+{
+ struct port_c *port = (struct port_c *)base;
+ *size = port->size;
+ if (*size == 0 || *size > 1) {
+   diag_set(ClientError, ER_SQL_FUNC_WRONG_RET_COUNT, "C", *size);
+   return NULL;
+ }
+ /** FIXME: Implement an ability to return a vector. */
+ assert(*size == 1);
+ struct region *region = &fiber()->gc;
+ size_t region_svp = region_used(region);
+ struct Mem *val = vdbemem_alloc_on_region(port->size);
+ if (val == NULL)
+   return NULL;
+ int i = 0;
+ const char *data;
+ struct port_c_entry *pe;
+ for (pe = port->first; pe != NULL; pe = pe->next) {
+   if (pe->mp_size == 0) {
+     data = tuple_data(pe->tuple);
+     if (mp_decode_array(&data) != 1) {
+       diag_set(ClientError, ER_SQL_EXECUTE,
+          "Unsupported type passed from C");
+       goto error;
+     }
+   } else {
+     data = pe->mp;
+   }
+   uint32_t len;
+   const char *str;
+   switch (mp_typeof(*data)) {
+   case MP_BOOL:
+     mem_set_bool(&val[i], mp_decode_bool(&data));
+     break;
+   case MP_FLOAT:
+     mem_set_double(&val[i], mp_decode_float(&data));
+     break;
+   case MP_DOUBLE:
+     mem_set_double(&val[i], mp_decode_double(&data));
+     break;
+   case MP_INT:
+     mem_set_i64(&val[i], mp_decode_int(&data));
+     break;
+   case MP_UINT:
+     mem_set_u64(&val[i], mp_decode_uint(&data));
+     break;
+   case MP_STR:
+     str = mp_decode_str(&data, &len);
+     if (sqlVdbeMemSetStr(&val[i], str, len,
+              1, SQL_TRANSIENT) != 0)
+       goto error;
+     break;
+   case MP_NIL:
+     sqlVdbeMemSetNull(&val[i]);
+     break;
+   default:
+     diag_set(ClientError, ER_SQL_EXECUTE,
+        "Unsupported type passed from C");
+     goto error;
+   }
+   i++;
+ }
+ return (struct sql_value *) val;
+error:
+ for (int i = 0; i < port->size; i++)
+   sqlVdbeMemRelease(&val[i]);
+ region_truncate(region, region_svp);
+ return NULL;
+}
diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h
new file mode 100644
index 000000000..acc8ce054
--- /dev/null
+++ b/src/box/sql/mem.h
@@ -0,0 +1,485 @@
+#pragma once
+/*
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "box/field_def.h"
+
+struct sql;
+struct Vdbe;
+struct region;
+struct mpstream;
+struct VdbeFrame;
+
+/*
+ * Internally, the vdbe manipulates nearly all SQL values as Mem
+ * structures. Each Mem struct may cache multiple representations (string,
+ * integer etc.) of the same value.
+ */
+struct Mem {
+ union MemValue {
+   double r; /* Real value used when MEM_Real is set in flags */
+   i64 i;    /* Integer value used when MEM_Int is set in flags */
+   uint64_t u; /* Unsigned integer used when MEM_UInt is set. */
+   bool b;         /* Boolean value used when MEM_Bool is set in flags */
+   int nZero;  /* Used when bit MEM_Zero is set in flags */
+   void *p;  /* Generic pointer */
+   /**
+    * A pointer to function implementation.
+    * Used only when flags==MEM_Agg.
+    */
+   struct func *func;
+   struct VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
+ } u;
+ u32 flags;    /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
+ /** Subtype for this value. */
+ enum sql_subtype subtype;
+ /**
+  * If value is fetched from tuple, then this property
+  * contains type of corresponding space's field. If it's
+  * value field_type_MAX then we can rely on on format
+  * (msgpack) type which is represented by 'flags'.
+  */
+ enum field_type field_type;
+ int n;      /* size (in bytes) of string value, excluding trailing '\0' */
+ char *z;    /* String or BLOB value */
+ /* ShallowCopy only needs to copy the information above */
+ char *zMalloc;    /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */
+ int szMalloc;   /* Size of the zMalloc allocation */
+ u32 uTemp;    /* Transient storage for serial_type in OP_MakeRecord */
+ sql *db;    /* The associated database connection */
+ void (*xDel) (void *);  /* Destructor for Mem.z - only valid if MEM_Dyn */
+#ifdef SQL_DEBUG
+ Mem *pScopyFrom;  /* This Mem is a shallow copy of pScopyFrom */
+ void *pFiller;    /* So that sizeof(Mem) is a multiple of 8 */
+#endif
+};
+
+/*
+ * Size of struct Mem not including the Mem.zMalloc member or anything that
+ * follows.
+ */
+#define MEMCELLSIZE offsetof(Mem,zMalloc)
+
+/* One or more of the following flags are set to indicate the validOK
+ * representations of the value stored in the Mem struct.
+ *
+ * If the MEM_Null flag is set, then the value is an SQL NULL value.
+ * No other flags may be set in this case.
+ *
+ * If the MEM_Str flag is set then Mem.z points at a string representation.
+ * Usually this is encoded in the same unicode encoding as the main
+ * database (see below for exceptions). If the MEM_Term flag is also
+ * set, then the string is nul terminated. The MEM_Int and MEM_Real
+ * flags may coexist with the MEM_Str flag.
+ */
+#define MEM_Null      0x0001 /* Value is NULL */
+#define MEM_Str       0x0002 /* Value is a string */
+#define MEM_Int       0x0004 /* Value is an integer */
+#define MEM_Real      0x0008 /* Value is a real number */
+#define MEM_Blob      0x0010 /* Value is a BLOB */
+#define MEM_Bool      0x0020    /* Value is a bool */
+#define MEM_UInt      0x0040 /* Value is an unsigned integer */
+#define MEM_Frame     0x0080 /* Value is a VdbeFrame object */
+#define MEM_Undefined 0x0100 /* Value is undefined */
+#define MEM_Cleared   0x0200 /* NULL set by OP_Null, not from data */
+#define MEM_TypeMask  0x83ff /* Mask of type bits */
+
+/* Whenever Mem contains a valid string or blob representation, one of
+ * the following flags must be set to determine the memory management
+ * policy for Mem.z.  The MEM_Term flag tells us whether or not the
+ * string is \000 or \u0000 terminated
+ */
+#define MEM_Term      0x0400 /* String rep is nul terminated */
+#define MEM_Dyn       0x0800 /* Need to call Mem.xDel() on Mem.z */
+#define MEM_Static    0x1000 /* Mem.z points to a static string */
+#define MEM_Ephem     0x2000 /* Mem.z points to an ephemeral string */
+#define MEM_Agg       0x4000 /* Mem.z points to an agg function context */
+#define MEM_Zero      0x8000 /* Mem.i contains count of 0s appended to blob */
+#define MEM_Subtype   0x10000  /* Mem.eSubtype is valid */
+#define MEM_Ptr       0x20000  /* Value is a generic pointer */
+
+/**
+ * In contrast to Mem_TypeMask, this one allows to get
+ * pure type of memory cell, i.e. without _Dyn/_Zero and other
+ * auxiliary flags.
+ */
+enum {
+ MEM_PURE_TYPE_MASK = 0x7f
+};
+
+static_assert(MEM_PURE_TYPE_MASK == (MEM_Null | MEM_Str | MEM_Int | MEM_Real |
+            MEM_Blob | MEM_Bool | MEM_UInt),
+       "value of type mask must consist of corresponding to memory "\
+       "type bits");
+
+/**
+ * Simple type to str convertor. It is used to simplify
+ * error reporting.
+ */
+char *
+mem_type_to_str(const struct Mem *p);
+
+/*
+ * Return the MP_type of the value of the MEM.
+ * Analogue of sql_value_type() but operates directly on
+ * transparent memory cell.
+ */
+enum mp_type
+mem_mp_type(struct Mem *mem);
+
+enum mp_type
+sql_value_type(struct Mem *);
+u16
+numericType(Mem *pMem);
+
+int sqlValueBytes(struct Mem *);
+
+#ifdef SQL_DEBUG
+int sqlVdbeCheckMemInvariants(struct Mem *);
+void sqlVdbeMemPrettyPrint(Mem * pMem, char *zBuf);
+void
+registerTrace(int iReg, Mem *p);
+
+/*
+ * Return true if a memory cell is not marked as invalid.  This macro
+ * is for use inside assert() statements only.
+ */
+#define memIsValid(M)  ((M)->flags & MEM_Undefined)==0
+#endif
+
+/**
+ * Try to convert a string value into a numeric representation
+ * if we can do so without loss of information. Firstly, value
+ * is attempted to be converted to integer, and in case of fail -
+ * to floating point number. Note that function is assumed to be
+ * called on memory cell containing string, i.e. mem->type == MEM_Str.
+ *
+ * @param record Memory cell containing value to be converted.
+ * @retval 0 If value can be converted to integer or number.
+ * @retval -1 Otherwise.
+ */
+int
+mem_apply_numeric_type(struct Mem *record);
+int sqlVdbeMemRealify(struct Mem *);
+
+/**
+ * Convert @a mem to NUMBER type, so that after conversion it has
+ * one of types MEM_Real, MEM_Int or MEM_UInt. If conversion is
+ * not possible, function returns -1.
+ *
+ * Beware - this function changes value and type of @a mem
+ * argument.
+ */
+int
+vdbe_mem_numerify(struct Mem *mem);
+
+int sqlVdbeMemCast(struct Mem *, enum field_type type);
+int mem_apply_integer_type(struct Mem *);
+int sqlVdbeMemStringify(struct Mem *);
+int sqlVdbeMemNulTerminate(struct Mem *);
+int sqlVdbeMemExpandBlob(struct Mem *);
+#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlVdbeMemExpandBlob(P):0)
+void sql_value_apply_type(struct Mem *val, enum field_type type);
+
+
+/**
+ * Processing is determined by the field type parameter:
+ *
+ * INTEGER:
+ *    If memory holds floating point value and it can be
+ *    converted without loss (2.0 - > 2), it's type is
+ *    changed to INT. Otherwise, simply return success status.
+ *
+ * NUMBER:
+ *    If memory holds INT or floating point value,
+ *    no actions take place.
+ *
+ * STRING:
+ *    Convert mem to a string representation.
+ *
+ * SCALAR:
+ *    Mem is unchanged, but flag is set to BLOB in case of
+ *    scalar-like type. Otherwise, (MAP, ARRAY) conversion
+ *    is impossible.
+ *
+ * BOOLEAN:
+ *    If memory holds BOOLEAN no actions take place.
+ *
+ * ANY:
+ *    Mem is unchanged, no actions take place.
+ *
+ * MAP/ARRAY:
+ *    These types can't be casted to scalar ones, or to each
+ *    other. So the only valid conversion is to type itself.
+ *
+ * @param record The value to apply type to.
+ * @param type The type to be applied.
+ */
+int
+mem_apply_type(struct Mem *record, enum field_type type);
+
+/**
+ * Convert the numeric value contained in MEM to another numeric
+ * type.
+ *
+ * @param mem The MEM that contains the numeric value.
+ * @param type The type to convert to.
+ * @retval 0 if the conversion was successful, -1 otherwise.
+ */
+int
+mem_convert_to_numeric(struct Mem *mem, enum field_type type);
+
+/** Setters = Change MEM value. */
+
+int sqlVdbeMemGrow(struct Mem * pMem, int n, int preserve);
+int sqlVdbeMemClearAndResize(struct Mem * pMem, int n);
+
+void
+mem_set_bool(struct Mem *mem, bool value);
+
+/**
+ * Set VDBE memory register with given pointer as a data.
+ * @param mem VDBE memory register to update.
+ * @param ptr Pointer to use.
+ */
+void
+mem_set_ptr(struct Mem *mem, void *ptr);
+
+/**
+ * Set integer value. Depending on its sign MEM_Int (in case
+ * of negative value) or MEM_UInt flag is set.
+ */
+void
+mem_set_i64(struct Mem *mem, int64_t value);
+
+/** Set unsigned value and MEM_UInt flag. */
+void
+mem_set_u64(struct Mem *mem, uint64_t value);
+
+/**
+ * Set integer value. According to is_neg flag value is considered
+ * to be signed or unsigned.
+ */
+void
+mem_set_int(struct Mem *mem, int64_t value, bool is_neg);
+
+/** Set double value and MEM_Real flag. */
+void
+mem_set_double(struct Mem *mem, double value);
+
+int
+sqlVdbeMemSetStr(struct Mem *, const char *, int, u8, void (*)(void *));
+void
+sqlVdbeMemInit(struct Mem *, sql *, u32);
+void
+sqlVdbeMemSetNull(struct Mem *);
+void
+sqlVdbeMemSetZeroBlob(struct Mem *, int);
+void sqlValueSetStr(struct Mem *, int, const void *,
+     void (*)(void *));
+void sqlValueSetNull(struct Mem *);
+void sqlValueFree(struct Mem *);
+struct Mem *sqlValueNew(struct sql *);
+
+/*
+ * Initialize an array of N Mem element.
+ */
+void
+initMemArray(Mem * p, int N, sql * db, u32 flags);
+
+/*
+ * Release an array of N Mem elements
+ */
+void
+releaseMemArray(Mem * p, int N);
+
+/*
+ * Clear any existing type flags from a Mem and replace them with f
+ */
+#define MemSetTypeFlag(p, f) \
+   ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f)
+
+/** Getters. */
+
+int
+mem_value_bool(const struct Mem *mem, bool *b);
+int sqlVdbeIntValue(struct Mem *, int64_t *, bool *is_neg);
+int sqlVdbeRealValue(struct Mem *, double *);
+const void *
+sql_value_blob(struct Mem *);
+
+int
+sql_value_bytes(struct Mem *);
+
+double
+sql_value_double(struct Mem *);
+
+bool
+sql_value_boolean(struct Mem *val);
+
+int
+sql_value_int(struct Mem *);
+
+sql_int64
+sql_value_int64(struct Mem *);
+
+uint64_t
+sql_value_uint64(struct Mem *val);
+
+const unsigned char *
+sql_value_text(struct Mem *);
+
+const void *sqlValueText(struct Mem *);
+
+/**
+ * Return pointer to a string with the data type in the case of
+ * binary data stored in @a value. Otherwise, return the result
+ * of sql_value_text(). It is used due to the fact that not all
+ * binary strings can be displayed correctly (e.g. contain
+ * unprintable symbols).
+ */
+const char *
+sql_value_to_diag_str(struct Mem *value);
+#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
+
+enum sql_subtype
+sql_value_subtype(sql_value * pVal);
+
+const Mem *
+columnNullValue(void);
+
+/** Checkers. */
+
+static inline bool
+sql_value_is_null(struct Mem *value)
+{
+ return sql_value_type(value) == MP_NIL;
+}
+
+int sqlVdbeMemTooBig(Mem *);
+
+/* Return TRUE if Mem X contains dynamically allocated content - anything
+ * that needs to be deallocated to avoid a leak.
+ */
+#define VdbeMemDynamic(X)  \
+  (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_Frame))!=0)
+
+
+int sqlMemCompare(const Mem *, const Mem *, const struct coll *);
+
+/**
+ * Check that MEM_type of the mem is compatible with given type.
+ *
+ * @param mem The MEM that contains the value to check.
+ * @param type The type to check.
+ * @retval TRUE if the MEM_type of the value and the given type
+ *         are compatible, FALSE otherwise.
+ */
+bool
+mem_is_type_compatible(struct Mem *mem, enum field_type type);
+
+/** MEM manipulate functions. */
+
+int
+vdbe_mem_alloc_blob_region(struct Mem *vdbe_mem, uint32_t size);
+int sqlVdbeMemCopy(Mem *, const Mem *);
+void sqlVdbeMemShallowCopy(Mem *, const Mem *, int);
+void sqlVdbeMemMove(Mem *, Mem *);
+int sqlVdbeMemMakeWriteable(Mem *);
+void sqlVdbeMemRelease(Mem * p);
+
+/**
+ * Memory cell mem contains the context of an aggregate function.
+ * This routine calls the finalize method for that function. The
+ * result of the aggregate is stored back into mem.
+ *
+ * Returns -1 if the finalizer reports an error. 0 otherwise.
+ */
+int
+sql_vdbemem_finalize(struct Mem *mem, struct func *func);
+
+/** MEM and msgpack functions. */
+
+/**
+ * Perform comparison of two keys: one is packed and one is not.
+ *
+ * @param key1 Pointer to pointer to first key.
+ * @param unpacked Pointer to unpacked tuple.
+ * @param key2_idx index of key in umpacked record to compare.
+ *
+ * @retval +1 if key1 > pUnpacked[iKey2], -1 ptherwise.
+ */
+int sqlVdbeCompareMsgpack(const char **key1,
+           struct UnpackedRecord *unpacked, int key2_idx);
+
+/**
+ * Perform comparison of two tuples: unpacked (key1) and packed (key2)
+ *
+ * @param key1 Packed key.
+ * @param unpacked Unpacked key.
+ *
+ * @retval +1 if key1 > unpacked, -1 otherwise.
+ */
+int sqlVdbeRecordCompareMsgpack(const void *key1,
+           struct UnpackedRecord *key2);
+
+/**
+ * Decode msgpack and save value into VDBE memory cell.
+ *
+ * @param buf Buffer to deserialize msgpack from.
+ * @param mem Memory cell to write value into.
+ * @param len[out] Length of decoded part.
+ * @retval Return code: < 0 in case of error.
+ * @retval 0 on success.
+ */
+int
+vdbe_decode_msgpack_into_mem(const char *buf, struct Mem *mem, uint32_t *len);
+
+/**
+ * Perform encoding memory variable to stream.
+ * @param stream Initialized mpstream encoder object.
+ * @param var Vdbe memory variable to encode with stream.
+ */
+void
+mpstream_encode_vdbe_mem(struct mpstream *stream, struct Mem *var);
+
+/**
+ * Perform encoding field_count Vdbe memory fields on region as
+ * msgpack array.
+ * @param fields The first Vdbe memory field to encode.
+ * @param field_count Count of fields to encode.
+ * @param[out] tuple_size Size of encoded tuple.
+ * @param region Region to use.
+ * @retval NULL on error, diag message is set.
+ * @retval Pointer to valid tuple on success.
+ */
+char *
+sql_vdbe_mem_encode_tuple(struct Mem *fields, uint32_t field_count,
+       uint32_t *tuple_size, struct region *region);
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index c15f2e0d1..b820b492e 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -39,6 +39,7 @@
 #include "box/coll_id_cache.h"
 #include "sqlInt.h"
 #include "tarantoolInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "box/schema.h"
 #include "box/session.h"
diff --git a/src/box/sql/printf.c b/src/box/sql/printf.c
index a3ff5bb09..cf32ba3f3 100644
--- a/src/box/sql/printf.c
+++ b/src/box/sql/printf.c
@@ -10,6 +10,7 @@
  * sql.
  */
 #include "sqlInt.h"
+#include "mem.h"
 
 /*
  * Conversion types fall into various categories as defined by the
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index fb2795103..0fa388ae9 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -36,6 +36,7 @@
 #include "coll/coll.h"
 #include "sqlInt.h"
 #include "tarantoolInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "box/box.h"
 #include "box/coll_id_cache.h"
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 95a6fedc9..c1a42fc2f 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -351,20 +351,6 @@ sql_stricmp(const char *, const char *);
 int
 sql_strnicmp(const char *, const char *, int);
 
- const void *
-sql_value_blob(sql_value *);
-
-int
-sql_value_bytes(sql_value *);
-
-double
-sql_value_double(sql_value *);
-
-bool
-sql_value_boolean(sql_value *val);
-
-int
-sql_value_int(sql_value *);
 
 /**
  * Get row column subtype.
@@ -375,34 +361,6 @@ sql_value_int(sql_value *);
 enum sql_subtype
 sql_column_subtype(struct sql_stmt *stmt, int i);
 
-sql_int64
-sql_value_int64(sql_value *);
-
-uint64_t
-sql_value_uint64(sql_value *val);
-
-const unsigned char *
-sql_value_text(sql_value *);
-
-/**
- * Return pointer to a string with the data type in the case of
- * binary data stored in @a value. Otherwise, return the result
- * of sql_value_text(). It is used due to the fact that not all
- * binary strings can be displayed correctly (e.g. contain
- * unprintable symbols).
- */
-const char *
-sql_value_to_diag_str(sql_value *value);
-
-enum mp_type
-sql_value_type(sql_value *);
-
-static inline bool
-sql_value_is_null(sql_value *value)
-{
- return sql_value_type(value) == MP_NIL;
-}
-
 sql *
 sql_context_db_handle(sql_context *);
 
@@ -4000,16 +3958,8 @@ int
 sql_rem_int(int64_t lhs, bool is_lhs_neg, int64_t rhs, bool is_rhs_neg,
      int64_t *res, bool *is_res_neg);
 
-const void *sqlValueText(sql_value *);
-int sqlValueBytes(sql_value *);
-void sqlValueSetStr(sql_value *, int, const void *,
-     void (*)(void *));
-void sqlValueSetNull(sql_value *);
-void sqlValueFree(sql_value *);
-sql_value *sqlValueNew(sql *);
 int sqlValueFromExpr(sql *, Expr *, enum field_type type,
       sql_value **);
-void sql_value_apply_type(sql_value *val, enum field_type type);
 
 extern const unsigned char sqlOpcodeProperty[];
 extern const unsigned char sqlUpperToLower[];
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 0c387bc3b..7c983fea5 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -37,6 +37,7 @@
 #include "box/schema.h"
 #include "sqlInt.h"
 #include "tarantoolInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 
 /* See comment in sqlInt.h */
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 18806b93f..775bce96f 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -46,6 +46,7 @@
 #include "box/tuple.h"
 #include "box/port.h"
 #include "sqlInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "tarantoolInt.h"
 
@@ -257,424 +258,6 @@ allocateCursor(
  return pCx;
 }
 
-int
-mem_apply_numeric_type(struct Mem *record)
-{
- if ((record->flags & MEM_Str) == 0)
-   return -1;
- int64_t integer_value;
- bool is_neg;
- if (sql_atoi64(record->z, &integer_value, &is_neg, record->n) == 0) {
-   mem_set_int(record, integer_value, is_neg);
-   return 0;
- }
- double float_value;
- if (sqlAtoF(record->z, &float_value, record->n) == 0)
-   return -1;
- mem_set_double(record, float_value);
- return 0;
-}
-
-/**
- * Processing is determined by the field type parameter:
- *
- * INTEGER:
- *    If memory holds floating point value and it can be
- *    converted without loss (2.0 - > 2), it's type is
- *    changed to INT. Otherwise, simply return success status.
- *
- * NUMBER:
- *    If memory holds INT or floating point value,
- *    no actions take place.
- *
- * STRING:
- *    Convert mem to a string representation.
- *
- * SCALAR:
- *    Mem is unchanged, but flag is set to BLOB in case of
- *    scalar-like type. Otherwise, (MAP, ARRAY) conversion
- *    is impossible.
- *
- * BOOLEAN:
- *    If memory holds BOOLEAN no actions take place.
- *
- * ANY:
- *    Mem is unchanged, no actions take place.
- *
- * MAP/ARRAY:
- *    These types can't be casted to scalar ones, or to each
- *    other. So the only valid conversion is to type itself.
- *
- * @param record The value to apply type to.
- * @param type The type to be applied.
- */
-static int
-mem_apply_type(struct Mem *record, enum field_type type)
-{
- if ((record->flags & MEM_Null) != 0)
-   return 0;
- assert(type < field_type_MAX);
- switch (type) {
- case FIELD_TYPE_INTEGER:
- case FIELD_TYPE_UNSIGNED:
-   if ((record->flags & (MEM_Bool | MEM_Blob)) != 0)
-     return -1;
-   if ((record->flags & MEM_UInt) == MEM_UInt)
-     return 0;
-   if ((record->flags & MEM_Real) == MEM_Real) {
-     double d = record->u.r;
-     if (d >= 0) {
-       if (double_compare_uint64(d, UINT64_MAX,
-               1) > 0)
-         return 0;
-       if ((double)(uint64_t)d == d)
-         mem_set_u64(record, (uint64_t)d);
-     } else {
-       if (double_compare_nint64(d, INT64_MIN, 1) < 0)
-         return 0;
-       if ((double)(int64_t)d == d)
-         mem_set_int(record, (int64_t)d, true);
-     }
-     return 0;
-   }
-   if ((record->flags & MEM_Str) != 0) {
-     bool is_neg;
-     int64_t i;
-     if (sql_atoi64(record->z, &i, &is_neg, record->n) != 0)
-       return -1;
-     mem_set_int(record, i, is_neg);
-   }
-   if ((record->flags & MEM_Int) == MEM_Int) {
-     if (type == FIELD_TYPE_UNSIGNED)
-       return -1;
-     return 0;
-   }
-   return 0;
- case FIELD_TYPE_BOOLEAN:
-   if ((record->flags & MEM_Bool) == MEM_Bool)
-     return 0;
-   return -1;
- case FIELD_TYPE_NUMBER:
-   if ((record->flags & (MEM_Real | MEM_Int | MEM_UInt)) != 0)
-     return 0;
-   return sqlVdbeMemRealify(record);
- case FIELD_TYPE_DOUBLE:
-   if ((record->flags & MEM_Real) != 0)
-     return 0;
-   return sqlVdbeMemRealify(record);
- case FIELD_TYPE_STRING:
-   /*
-    * Only attempt the conversion to TEXT if there is
-    * an integer or real representation (BLOB and
-    * NULL do not get converted).
-    */
-   if ((record->flags & MEM_Str) == 0 &&
-       (record->flags & (MEM_Real | MEM_Int | MEM_UInt)) != 0)
-     sqlVdbeMemStringify(record);
-   record->flags &= ~(MEM_Real | MEM_Int | MEM_UInt);
-   return 0;
- case FIELD_TYPE_VARBINARY:
-   if ((record->flags & MEM_Blob) == 0)
-     return -1;
-   return 0;
- case FIELD_TYPE_SCALAR:
-   /* Can't cast MAP and ARRAY to scalar types. */
-   if ((record->flags & MEM_Subtype) != 0 &&
-       record->subtype == SQL_SUBTYPE_MSGPACK) {
-     assert(mp_typeof(*record->z) == MP_MAP ||
-            mp_typeof(*record->z) == MP_ARRAY);
-     return -1;
-   }
-   return 0;
- case FIELD_TYPE_MAP:
-   if ((record->flags & MEM_Subtype) != 0 &&
-       record->subtype == SQL_SUBTYPE_MSGPACK &&
-       mp_typeof(*record->z) == MP_MAP)
-     return 0;
-   return -1;
- case FIELD_TYPE_ARRAY:
-   if ((record->flags & MEM_Subtype) != 0 &&
-       record->subtype == SQL_SUBTYPE_MSGPACK &&
-       mp_typeof(*record->z) == MP_ARRAY)
-     return 0;
-   return -1;
- case FIELD_TYPE_ANY:
-   return 0;
- default:
-   return -1;
- }
-}
-
-/*
- * Exported version of mem_apply_type(). This one works on sql_value*,
- * not the internal Mem* type.
- */
-void
-sql_value_apply_type(
- sql_value *pVal,
- enum field_type type)
-{
- mem_apply_type((Mem *) pVal, type);
-}
-
-/**
- * Check that MEM_type of the mem is compatible with given type.
- *
- * @param mem The MEM that contains the value to check.
- * @param type The type to check.
- * @retval TRUE if the MEM_type of the value and the given type
- *         are compatible, FALSE otherwise.
- */
-static bool
-mem_is_type_compatible(struct Mem *mem, enum field_type type)
-{
- enum mp_type mp_type = mem_mp_type(mem);
- assert(mp_type < MP_EXT);
- return field_mp_plain_type_is_compatible(type, mp_type, true);
-}
-
-/**
- * Convert the numeric value contained in MEM to double.
- *
- * @param mem The MEM that contains the numeric value.
- * @retval 0 if the conversion was successful, -1 otherwise.
- */
-static int
-mem_convert_to_double(struct Mem *mem)
-{
- if ((mem->flags & MEM_Real) != 0)
-   return 0;
- if ((mem->flags & (MEM_Int | MEM_UInt)) == 0)
-   return -1;
- double d;
- if ((mem->flags & MEM_Int) != 0)
-   d = (double)mem->u.i;
- else
-   d = (double)mem->u.u;
- mem_set_double(mem, d);
- return 0;
-}
-
-/**
- * Convert the numeric value contained in MEM to unsigned.
- *
- * @param mem The MEM that contains the numeric value.
- * @retval 0 if the conversion was successful, -1 otherwise.
- */
-static int
-mem_convert_to_unsigned(struct Mem *mem)
-{
- if ((mem->flags & MEM_UInt) != 0)
-   return 0;
- if ((mem->flags & MEM_Int) != 0)
-   return -1;
- if ((mem->flags & MEM_Real) == 0)
-   return -1;
- double d = mem->u.r;
- if (d < 0.0 || d >= (double)UINT64_MAX)
-   return -1;
- mem_set_u64(mem, (uint64_t) d);
- return 0;
-}
-
-/**
- * Convert the numeric value contained in MEM to integer.
- *
- * @param mem The MEM that contains the numeric value.
- * @retval 0 if the conversion was successful, -1 otherwise.
- */
-static int
-mem_convert_to_integer(struct Mem *mem)
-{
- if ((mem->flags & (MEM_UInt | MEM_Int)) != 0)
-   return 0;
- if ((mem->flags & MEM_Real) == 0)
-   return -1;
- double d = mem->u.r;
- if (d >= (double)UINT64_MAX || d < (double)INT64_MIN)
-   return -1;
- if (d < (double)INT64_MAX)
-   mem_set_int(mem, (int64_t) d, d < 0);
- else
-   mem_set_int(mem, (uint64_t) d, false);
- return 0;
-}
-
-/**
- * Convert the numeric value contained in MEM to another numeric
- * type.
- *
- * @param mem The MEM that contains the numeric value.
- * @param type The type to convert to.
- * @retval 0 if the conversion was successful, -1 otherwise.
- */
-static int
-mem_convert_to_numeric(struct Mem *mem, enum field_type type)
-{
- assert(mp_type_is_numeric(mem_mp_type(mem)) &&
-        sql_type_is_numeric(type));
- assert(type != FIELD_TYPE_NUMBER);
- if (type == FIELD_TYPE_DOUBLE)
-   return mem_convert_to_double(mem);
- if (type == FIELD_TYPE_UNSIGNED)
-   return mem_convert_to_unsigned(mem);
- assert(type == FIELD_TYPE_INTEGER);
- return mem_convert_to_integer(mem);
-}
-
-/*
- * pMem currently only holds a string type (or maybe a BLOB that we can
- * interpret as a string if we want to).  Compute its corresponding
- * numeric type, if has one.  Set the pMem->u.r and pMem->u.i fields
- * accordingly.
- */
-static u16 SQL_NOINLINE computeNumericType(Mem *pMem)
-{
- assert((pMem->flags & (MEM_Int | MEM_UInt | MEM_Real)) == 0);
- assert((pMem->flags & (MEM_Str|MEM_Blob))!=0);
- if (sqlAtoF(pMem->z, &pMem->u.r, pMem->n)==0)
-   return 0;
- bool is_neg;
- if (sql_atoi64(pMem->z, (int64_t *) &pMem->u.i, &is_neg, pMem->n) == 0)
-   return is_neg ? MEM_Int : MEM_UInt;
- return MEM_Real;
-}
-
-/*
- * Return the numeric type for pMem, either MEM_Int or MEM_Real or both or
- * none.
- *
- * Unlike mem_apply_numeric_type(), this routine does not modify pMem->flags.
- * But it does set pMem->u.r and pMem->u.i appropriately.
- */
-static u16 numericType(Mem *pMem)
-{
- if ((pMem->flags & (MEM_Int | MEM_UInt | MEM_Real)) != 0)
-   return pMem->flags & (MEM_Int | MEM_UInt | MEM_Real);
- if (pMem->flags & (MEM_Str|MEM_Blob)) {
-   return computeNumericType(pMem);
- }
- return 0;
-}
-
-#ifdef SQL_DEBUG
-/*
- * Write a nice string representation of the contents of cell pMem
- * into buffer zBuf, length nBuf.
- */
-void
-sqlVdbeMemPrettyPrint(Mem *pMem, char *zBuf)
-{
- char *zCsr = zBuf;
- int f = pMem->flags;
-
- if (f&MEM_Blob) {
-   int i;
-   char c;
-   if (f & MEM_Dyn) {
-     c = 'z';
-     assert((f & (MEM_Static|MEM_Ephem))==0);
-   } else if (f & MEM_Static) {
-     c = 't';
-     assert((f & (MEM_Dyn|MEM_Ephem))==0);
-   } else if (f & MEM_Ephem) {
-     c = 'e';
-     assert((f & (MEM_Static|MEM_Dyn))==0);
-   } else {
-     c = 's';
-   }
-
-   sql_snprintf(100, zCsr, "%c", c);
-   zCsr += sqlStrlen30(zCsr);
-   sql_snprintf(100, zCsr, "%d[", pMem->n);
-   zCsr += sqlStrlen30(zCsr);
-   for(i=0; i<16 && i<pMem->n; i++) {
-     sql_snprintf(100, zCsr, "%02X", ((int)pMem->z[i] & 0xFF));
-     zCsr += sqlStrlen30(zCsr);
-   }
-   for(i=0; i<16 && i<pMem->n; i++) {
-     char z = pMem->z[i];
-     if (z<32 || z>126) *zCsr++ = '.';
-     else *zCsr++ = z;
-   }
-   sql_snprintf(100, zCsr, "]%s", "(8)");
-   zCsr += sqlStrlen30(zCsr);
-   if (f & MEM_Zero) {
-     sql_snprintf(100, zCsr,"+%dz",pMem->u.nZero);
-     zCsr += sqlStrlen30(zCsr);
-   }
-   *zCsr = '\0';
- } else if (f & MEM_Str) {
-   int j, k;
-   zBuf[0] = ' ';
-   if (f & MEM_Dyn) {
-     zBuf[1] = 'z';
-     assert((f & (MEM_Static|MEM_Ephem))==0);
-   } else if (f & MEM_Static) {
-     zBuf[1] = 't';
-     assert((f & (MEM_Dyn|MEM_Ephem))==0);
-   } else if (f & MEM_Ephem) {
-     zBuf[1] = 'e';
-     assert((f & (MEM_Static|MEM_Dyn))==0);
-   } else {
-     zBuf[1] = 's';
-   }
-   k = 2;
-   sql_snprintf(100, &zBuf[k], "%d", pMem->n);
-   k += sqlStrlen30(&zBuf[k]);
-   zBuf[k++] = '[';
-   for(j=0; j<15 && j<pMem->n; j++) {
-     u8 c = pMem->z[j];
-     if (c>=0x20 && c<0x7f) {
-       zBuf[k++] = c;
-     } else {
-       zBuf[k++] = '.';
-     }
-   }
-   zBuf[k++] = ']';
-   sql_snprintf(100,&zBuf[k],"(8)");
-   k += sqlStrlen30(&zBuf[k]);
-   zBuf[k++] = 0;
- }
-}
-#endif
-
-#ifdef SQL_DEBUG
-/*
- * Print the value of a register for tracing purposes:
- */
-static void
-memTracePrint(Mem *p)
-{
- if (p->flags & MEM_Undefined) {
-   printf(" undefined");
- } else if (p->flags & MEM_Null) {
-   printf(" NULL");
- } else if ((p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str)) {
-   printf(" si:%lld", p->u.i);
- } else if (p->flags & MEM_Int) {
-   printf(" i:%lld", p->u.i);
- } else if (p->flags & MEM_UInt) {
-   printf(" u:%"PRIu64"", p->u.u);
- } else if (p->flags & MEM_Real) {
-   printf(" r:%g", p->u.r);
- } else if (p->flags & MEM_Bool) {
-   printf(" bool:%s", SQL_TOKEN_BOOLEAN(p->u.b));
- } else {
-   char zBuf[200];
-   sqlVdbeMemPrettyPrint(p, zBuf);
-   printf(" %s", zBuf);
- }
- if (p->flags & MEM_Subtype) printf(" subtype=0x%02x", p->subtype);
-}
-static void
-registerTrace(int iReg, Mem *p) {
- printf("REG[%d] = ", iReg);
- memTracePrint(p);
- printf("\n");
-}
-#endif
-
 #ifdef SQL_DEBUG
 #  define REGISTER_TRACE(P,R,M)          \
  if(P->sql_flags&SQL_VdbeTrace) registerTrace(R,M);
@@ -727,43 +310,6 @@ vdbe_add_new_autoinc_id(struct Vdbe *vdbe, int64_t id)
  return 0;
 }
 
-char *
-mem_type_to_str(const struct Mem *p)
-{
- assert(p != NULL);
- switch (p->flags & MEM_PURE_TYPE_MASK) {
- case MEM_Null:
-   return "NULL";
- case MEM_Str:
-   return "text";
- case MEM_Int:
-   return "integer";
- case MEM_UInt:
-   return "unsigned";
- case MEM_Real:
-   return "real";
- case MEM_Blob:
-   return "varbinary";
- case MEM_Bool:
-   return "boolean";
- default:
-   unreachable();
- }
-}
-
-/* Allocate memory for internal VDBE structure on region. */
-static int
-vdbe_mem_alloc_blob_region(struct Mem *vdbe_mem, uint32_t size)
-{
- vdbe_mem->n = size;
- vdbe_mem->z = region_alloc(&fiber()->gc, size);
- if (vdbe_mem->z == NULL)
-   return -1;
- vdbe_mem->flags = MEM_Ephem | MEM_Blob;
- assert(sqlVdbeCheckMemInvariants(vdbe_mem));
- return 0;
-}
-
 static inline const struct tuple_field *
 vdbe_field_ref_fetch_field(struct vdbe_field_ref *field_ref, uint32_t fieldno)
 {
diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
index 48815d4ec..118f1cd83 100644
--- a/src/box/sql/vdbe.h
+++ b/src/box/sql/vdbe.h
@@ -268,7 +268,6 @@ void sqlVdbeSwap(Vdbe *, Vdbe *);
 VdbeOp *sqlVdbeTakeOpArray(Vdbe *, int *, int *);
 sql_value *sqlVdbeGetBoundValue(Vdbe *, int, u8);
 char *sqlVdbeExpandSql(Vdbe *, const char *);
-int sqlMemCompare(const Mem *, const Mem *, const struct coll *);
 
 /**
  * Perform unpacking of provided message pack.
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index 7205f1af3..b2d4f27c1 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -155,151 +155,6 @@ struct VdbeFrame {
  int nDbChange;    /* Value of db->nChange */
 };
 
-#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
-
-/*
- * Internally, the vdbe manipulates nearly all SQL values as Mem
- * structures. Each Mem struct may cache multiple representations (string,
- * integer etc.) of the same value.
- */
-struct Mem {
- union MemValue {
-   double r; /* Real value used when MEM_Real is set in flags */
-   i64 i;    /* Integer value used when MEM_Int is set in flags */
-   uint64_t u; /* Unsigned integer used when MEM_UInt is set. */
-   bool b;         /* Boolean value used when MEM_Bool is set in flags */
-   int nZero;  /* Used when bit MEM_Zero is set in flags */
-   void *p;  /* Generic pointer */
-   /**
-    * A pointer to function implementation.
-    * Used only when flags==MEM_Agg.
-    */
-   struct func *func;
-   VdbeFrame *pFrame;  /* Used when flags==MEM_Frame */
- } u;
- u32 flags;    /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
- /** Subtype for this value. */
- enum sql_subtype subtype;
- /**
-  * If value is fetched from tuple, then this property
-  * contains type of corresponding space's field. If it's
-  * value field_type_MAX then we can rely on on format
-  * (msgpack) type which is represented by 'flags'.
-  */
- enum field_type field_type;
- int n;      /* size (in bytes) of string value, excluding trailing '\0' */
- char *z;    /* String or BLOB value */
- /* ShallowCopy only needs to copy the information above */
- char *zMalloc;    /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */
- int szMalloc;   /* Size of the zMalloc allocation */
- u32 uTemp;    /* Transient storage for serial_type in OP_MakeRecord */
- sql *db;    /* The associated database connection */
- void (*xDel) (void *);  /* Destructor for Mem.z - only valid if MEM_Dyn */
-#ifdef SQL_DEBUG
- Mem *pScopyFrom;  /* This Mem is a shallow copy of pScopyFrom */
- void *pFiller;    /* So that sizeof(Mem) is a multiple of 8 */
-#endif
-};
-
-/*
- * Size of struct Mem not including the Mem.zMalloc member or anything that
- * follows.
- */
-#define MEMCELLSIZE offsetof(Mem,zMalloc)
-
-/* One or more of the following flags are set to indicate the validOK
- * representations of the value stored in the Mem struct.
- *
- * If the MEM_Null flag is set, then the value is an SQL NULL value.
- * No other flags may be set in this case.
- *
- * If the MEM_Str flag is set then Mem.z points at a string representation.
- * Usually this is encoded in the same unicode encoding as the main
- * database (see below for exceptions). If the MEM_Term flag is also
- * set, then the string is nul terminated. The MEM_Int and MEM_Real
- * flags may coexist with the MEM_Str flag.
- */
-#define MEM_Null      0x0001 /* Value is NULL */
-#define MEM_Str       0x0002 /* Value is a string */
-#define MEM_Int       0x0004 /* Value is an integer */
-#define MEM_Real      0x0008 /* Value is a real number */
-#define MEM_Blob      0x0010 /* Value is a BLOB */
-#define MEM_Bool      0x0020    /* Value is a bool */
-#define MEM_UInt      0x0040 /* Value is an unsigned integer */
-#define MEM_Frame     0x0080 /* Value is a VdbeFrame object */
-#define MEM_Undefined 0x0100 /* Value is undefined */
-#define MEM_Cleared   0x0200 /* NULL set by OP_Null, not from data */
-#define MEM_TypeMask  0x83ff /* Mask of type bits */
-
-/* Whenever Mem contains a valid string or blob representation, one of
- * the following flags must be set to determine the memory management
- * policy for Mem.z.  The MEM_Term flag tells us whether or not the
- * string is \000 or \u0000 terminated
- */
-#define MEM_Term      0x0400 /* String rep is nul terminated */
-#define MEM_Dyn       0x0800 /* Need to call Mem.xDel() on Mem.z */
-#define MEM_Static    0x1000 /* Mem.z points to a static string */
-#define MEM_Ephem     0x2000 /* Mem.z points to an ephemeral string */
-#define MEM_Agg       0x4000 /* Mem.z points to an agg function context */
-#define MEM_Zero      0x8000 /* Mem.i contains count of 0s appended to blob */
-#define MEM_Subtype   0x10000  /* Mem.eSubtype is valid */
-#define MEM_Ptr       0x20000  /* Value is a generic pointer */
-
-/**
- * In contrast to Mem_TypeMask, this one allows to get
- * pure type of memory cell, i.e. without _Dyn/_Zero and other
- * auxiliary flags.
- */
-enum {
- MEM_PURE_TYPE_MASK = 0x7f
-};
-
-static_assert(MEM_PURE_TYPE_MASK == (MEM_Null | MEM_Str | MEM_Int | MEM_Real |
-            MEM_Blob | MEM_Bool | MEM_UInt),
-       "value of type mask must consist of corresponding to memory "\
-       "type bits");
-
-/**
- * Simple type to str convertor. It is used to simplify
- * error reporting.
- */
-char *
-mem_type_to_str(const struct Mem *p);
-
-/**
- * Try to convert a string value into a numeric representation
- * if we can do so without loss of information. Firstly, value
- * is attempted to be converted to integer, and in case of fail -
- * to floating point number. Note that function is assumed to be
- * called on memory cell containing string, i.e. mem->type == MEM_Str.
- *
- * @param record Memory cell containing value to be converted.
- * @retval 0 If value can be converted to integer or number.
- * @retval -1 Otherwise.
- */
-int
-mem_apply_numeric_type(struct Mem *record);
-
-/* Return TRUE if Mem X contains dynamically allocated content - anything
- * that needs to be deallocated to avoid a leak.
- */
-#define VdbeMemDynamic(X)  \
-  (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_Frame))!=0)
-
-/*
- * Clear any existing type flags from a Mem and replace them with f
- */
-#define MemSetTypeFlag(p, f) \
-   ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f)
-
-/*
- * Return true if a memory cell is not marked as invalid.  This macro
- * is for use inside assert() statements only.
- */
-#ifdef SQL_DEBUG
-#define memIsValid(M)  ((M)->flags & MEM_Undefined)==0
-#endif
-
 /*
  * The "context" argument for an installable function.  A pointer to an
  * instance of this structure is the first argument to the routines used
@@ -477,83 +332,8 @@ int sqlVdbeExec(Vdbe *);
 int sqlVdbeList(Vdbe *);
 
 int sqlVdbeHalt(Vdbe *);
-int sqlVdbeMemTooBig(Mem *);
-int sqlVdbeMemCopy(Mem *, const Mem *);
-void sqlVdbeMemShallowCopy(Mem *, const Mem *, int);
-void sqlVdbeMemMove(Mem *, Mem *);
-int sqlVdbeMemNulTerminate(Mem *);
-int sqlVdbeMemSetStr(Mem *, const char *, int, u8, void (*)(void *));
 
-void
-mem_set_bool(struct Mem *mem, bool value);
-
-/**
- * Set VDBE memory register with given pointer as a data.
- * @param mem VDBE memory register to update.
- * @param ptr Pointer to use.
- */
-void
-mem_set_ptr(struct Mem *mem, void *ptr);
-
-/**
- * Set integer value. Depending on its sign MEM_Int (in case
- * of negative value) or MEM_UInt flag is set.
- */
-void
-mem_set_i64(struct Mem *mem, int64_t value);
-
-/** Set unsigned value and MEM_UInt flag. */
-void
-mem_set_u64(struct Mem *mem, uint64_t value);
-
-/**
- * Set integer value. According to is_neg flag value is considered
- * to be signed or unsigned.
- */
-void
-mem_set_int(struct Mem *mem, int64_t value, bool is_neg);
-
-/** Set double value and MEM_Real flag. */
-void
-mem_set_double(struct Mem *mem, double value);
-
-void sqlVdbeMemInit(Mem *, sql *, u32);
-void sqlVdbeMemSetNull(Mem *);
-void sqlVdbeMemSetZeroBlob(Mem *, int);
-int sqlVdbeMemMakeWriteable(Mem *);
-int sqlVdbeMemStringify(Mem *);
-int sqlVdbeIntValue(Mem *, int64_t *, bool *is_neg);
-
-int sqlVdbeRealValue(Mem *, double *);
-
-int
-mem_value_bool(const struct Mem *mem, bool *b);
-
-int mem_apply_integer_type(Mem *);
-int sqlVdbeMemRealify(Mem *);
-
-/**
- * Convert @a mem to NUMBER type, so that after conversion it has
- * one of types MEM_Real, MEM_Int or MEM_UInt. If conversion is
- * not possible, function returns -1.
- *
- * Beware - this function changes value and type of @a mem
- * argument.
- */
-int
-vdbe_mem_numerify(struct Mem *mem);
-
-int sqlVdbeMemCast(Mem *, enum field_type type);
 int sqlVdbeMemFromBtree(BtCursor *, u32, u32, Mem *);
-void sqlVdbeMemRelease(Mem * p);
-
-/*
- * Return the MP_type of the value of the MEM.
- * Analogue of sql_value_type() but operates directly on
- * transparent memory cell.
- */
-enum mp_type
-mem_mp_type(struct Mem *mem);
 
 /**
  * In terms of VDBE memory cell type, _BIN, _ARRAY and _MAP
@@ -566,19 +346,7 @@ mem_mp_type(struct Mem *mem);
 #define mp_type_is_numeric(X) ((X) == MP_INT || (X) == MP_UINT ||\
             (X) == MP_DOUBLE)
 
-/**
- * Memory cell mem contains the context of an aggregate function.
- * This routine calls the finalize method for that function. The
- * result of the aggregate is stored back into mem.
- *
- * Returns -1 if the finalizer reports an error. 0 otherwise.
- */
-int
-sql_vdbemem_finalize(struct Mem *mem, struct func *func);
-
 const char *sqlOpcodeName(int);
-int sqlVdbeMemGrow(Mem * pMem, int n, int preserve);
-int sqlVdbeMemClearAndResize(Mem * pMem, int n);
 int sqlVdbeCloseStatement(Vdbe *, int);
 void sqlVdbeFrameDelete(VdbeFrame *);
 int sqlVdbeFrameRestore(VdbeFrame *);
@@ -598,7 +366,6 @@ int sqlVdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
 
 #ifdef SQL_DEBUG
 void sqlVdbeMemAboutToChange(Vdbe *, Mem *);
-int sqlVdbeCheckMemInvariants(Mem *);
 #endif
 
 int sqlVdbeCheckFk(Vdbe *, int);
@@ -606,48 +373,9 @@ int sqlVdbeCheckFk(Vdbe *, int);
 int sqlVdbeMemTranslate(Mem *, u8);
 #ifdef SQL_DEBUG
 void sqlVdbePrintSql(Vdbe *);
-void sqlVdbeMemPrettyPrint(Mem * pMem, char *zBuf);
 #endif
 int sqlVdbeMemHandleBom(Mem * pMem);
 
-int sqlVdbeMemExpandBlob(Mem *);
-#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlVdbeMemExpandBlob(P):0)
-
-/**
- * Perform comparison of two keys: one is packed and one is not.
- *
- * @param key1 Pointer to pointer to first key.
- * @param unpacked Pointer to unpacked tuple.
- * @param key2_idx index of key in umpacked record to compare.
- *
- * @retval +1 if key1 > pUnpacked[iKey2], -1 ptherwise.
- */
-int sqlVdbeCompareMsgpack(const char **key1,
-           struct UnpackedRecord *unpacked, int key2_idx);
-
-/**
- * Perform comparison of two tuples: unpacked (key1) and packed (key2)
- *
- * @param key1 Packed key.
- * @param unpacked Unpacked key.
- *
- * @retval +1 if key1 > unpacked, -1 otherwise.
- */
-int sqlVdbeRecordCompareMsgpack(const void *key1,
-           struct UnpackedRecord *key2);
-
-/**
- * Decode msgpack and save value into VDBE memory cell.
- *
- * @param buf Buffer to deserialize msgpack from.
- * @param mem Memory cell to write value into.
- * @param len[out] Length of decoded part.
- * @retval Return code: < 0 in case of error.
- * @retval 0 on success.
- */
-int
-vdbe_decode_msgpack_into_mem(const char *buf, struct Mem *mem, uint32_t *len);
-
 struct mpstream;
 struct region;
 
@@ -658,26 +386,4 @@ set_encode_error(void *error_ctx)
  *(bool *)error_ctx = true;
 }
 
-/**
- * Perform encoding memory variable to stream.
- * @param stream Initialized mpstream encoder object.
- * @param var Vdbe memory variable to encode with stream.
- */
-void
-mpstream_encode_vdbe_mem(struct mpstream *stream, struct Mem *var);
-
-/**
- * Perform encoding field_count Vdbe memory fields on region as
- * msgpack array.
- * @param fields The first Vdbe memory field to encode.
- * @param field_count Count of fields to encode.
- * @param[out] tuple_size Size of encoded tuple.
- * @param region Region to use.
- * @retval NULL on error, diag message is set.
- * @retval Pointer to valid tuple on success.
- */
-char *
-sql_vdbe_mem_encode_tuple(struct Mem *fields, uint32_t field_count,
-       uint32_t *tuple_size, struct region *region);
-
 #endif       /* !defined(SQL_VDBEINT_H) */
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index 7c59ef83f..24fa99f17 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -35,6 +35,7 @@
  * VDBE.
  */
 #include "sqlInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "box/session.h"
 
@@ -122,101 +123,6 @@ 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.
- */
-const void *
-sql_value_blob(sql_value * pVal)
-{
- Mem *p = (Mem *) pVal;
- if (p->flags & (MEM_Blob | MEM_Str)) {
-   if (ExpandBlob(p) != 0) {
-     assert(p->flags == MEM_Null && p->z == 0);
-     return 0;
-   }
-   p->flags |= MEM_Blob;
-   return p->n ? p->z : 0;
- } else {
-   return sql_value_text(pVal);
- }
-}
-
-int
-sql_value_bytes(sql_value * pVal)
-{
- return sqlValueBytes(pVal);
-}
-
-double
-sql_value_double(sql_value * pVal)
-{
- double v = 0.0;
- sqlVdbeRealValue((Mem *) pVal, &v);
- return v;
-}
-
-bool
-sql_value_boolean(sql_value *val)
-{
- bool b = false;
- int rc = mem_value_bool((struct Mem *) val, &b);
- assert(rc == 0);
- (void) rc;
- return b;
-}
-
-int
-sql_value_int(sql_value * pVal)
-{
- int64_t i = 0;
- bool is_neg;
- sqlVdbeIntValue((Mem *) pVal, &i, &is_neg);
- return (int)i;
-}
-
-sql_int64
-sql_value_int64(sql_value * pVal)
-{
- int64_t i = 0;
- bool unused;
- sqlVdbeIntValue((Mem *) pVal, &i, &unused);
- return i;
-}
-
-uint64_t
-sql_value_uint64(sql_value *val)
-{
- int64_t i = 0;
- bool is_neg;
- sqlVdbeIntValue((struct Mem *) val, &i, &is_neg);
- assert(!is_neg);
- return i;
-}
-
-enum sql_subtype
-sql_value_subtype(sql_value * pVal)
-{
- return (pVal->flags & MEM_Subtype) != 0 ? pVal->subtype : SQL_SUBTYPE_NO;
-}
-
-const unsigned char *
-sql_value_text(sql_value * pVal)
-{
- return (const unsigned char *)sqlValueText(pVal);
-}
-
-/* EVIDENCE-OF: R-12793-43283 Every value in sql has one of five
- * fundamental datatypes: 64-bit signed integer 64-bit IEEE floating
- * point number string BLOB NULL
- */
-enum mp_type
-sql_value_type(sql_value *pVal)
-{
- struct Mem *mem = (struct Mem *) pVal;
- return mem_mp_type(mem);
-}
-
 /* Make a copy of an sql_value object
  */
 sql_value *
@@ -582,47 +488,6 @@ sql_data_count(sql_stmt * pStmt)
  return pVm->nResColumn;
 }
 
-/*
- * Return a pointer to static memory containing an SQL NULL value.
- */
-static const Mem *
-columnNullValue(void)
-{
- /* Even though the Mem structure contains an element
-  * of type i64, on certain architectures (x86) with certain compiler
-  * switches (-Os), gcc may align this Mem object on a 4-byte boundary
-  * instead of an 8-byte one. This all works fine, except that when
-  * running with SQL_DEBUG defined the sql code sometimes assert()s
-  * that a Mem structure is located on an 8-byte boundary. To prevent
-  * these assert()s from failing, when building with SQL_DEBUG defined
-  * using gcc, we force nullMem to be 8-byte aligned using the magical
-  * __attribute__((aligned(8))) macro.
-  */
- static const Mem nullMem
-#if defined(SQL_DEBUG) && defined(__GNUC__)
-     __attribute__ ((aligned(8)))
-#endif
-     = {
-   /* .u          = */  {
-   0},
-       /* .flags      = */ (u16) MEM_Null,
-       /* .eSubtype   = */ (u8) 0,
-       /* .field_type = */ field_type_MAX,
-       /* .n          = */ (int)0,
-       /* .z          = */ (char *)0,
-       /* .zMalloc    = */ (char *)0,
-       /* .szMalloc   = */ (int)0,
-       /* .uTemp      = */ (u32) 0,
-       /* .db         = */ (sql *) 0,
-       /* .xDel       = */ (void (*)(void *))0,
-#ifdef SQL_DEBUG
-       /* .pScopyFrom = */ (Mem *) 0,
-       /* .pFiller    = */ (void *)0,
-#endif
- };
- return &nullMem;
-}
-
 /*
  * Check to see if column iCol of the given statement is valid.  If
  * it is, return a pointer to the Mem for the value of that column.
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 772476377..79c3d60e0 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -41,6 +41,7 @@
 #include "box/txn.h"
 #include "msgpuck/msgpuck.h"
 #include "sqlInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "tarantoolInt.h"
 #include "box/execute.h"
@@ -1194,65 +1195,6 @@ sqlVdbePrintOp(FILE * pOut, int pc, Op * pOp)
 }
 #endif
 
-/*
- * Initialize an array of N Mem element.
- */
-static void
-initMemArray(Mem * p, int N, sql * db, u32 flags)
-{
- while ((N--) > 0) {
-   p->db = db;
-   p->flags = flags;
-   p->szMalloc = 0;
-   p->field_type = field_type_MAX;
-#ifdef SQL_DEBUG
-   p->pScopyFrom = 0;
-#endif
-   p++;
- }
-}
-
-/*
- * Release an array of N Mem elements
- */
-static void
-releaseMemArray(Mem * p, int N)
-{
- if (p && N) {
-   Mem *pEnd = &p[N];
-   sql *db = p->db;
-   do {
-     assert((&p[1]) == pEnd || p[0].db == p[1].db);
-     assert(sqlVdbeCheckMemInvariants(p));
-
-     /* This block is really an inlined version of sqlVdbeMemRelease()
-      * that takes advantage of the fact that the memory cell value is
-      * being set to NULL after releasing any dynamic resources.
-      *
-      * The justification for duplicating code is that according to
-      * callgrind, this causes a certain test case to hit the CPU 4.7
-      * percent less (x86 linux, gcc version 4.1.2, -O6) than if
-      * sqlMemRelease() were called from here. With -O2, this jumps
-      * to 6.6 percent. The test case is inserting 1000 rows into a table
-      * with no indexes using a single prepared INSERT statement, bind()
-      * and reset(). Inserts are grouped into a transaction.
-      */
-     testcase(p->flags & MEM_Agg);
-     testcase(p->flags & MEM_Dyn);
-     testcase(p->flags & MEM_Frame);
-     if (p->
-         flags & (MEM_Agg | MEM_Dyn | MEM_Frame)) {
-       sqlVdbeMemRelease(p);
-     } else if (p->szMalloc) {
-       sqlDbFree(db, p->zMalloc);
-       p->szMalloc = 0;
-     }
-
-     p->flags = MEM_Undefined;
-   } while ((++p) < pEnd);
- }
-}
-
 /*
  * Delete a VdbeFrame object and its contents. VdbeFrame objects are
  * allocated by the OP_Program opcode in sqlVdbeExec().
@@ -2334,204 +2276,6 @@ sqlVdbeAllocUnpackedRecord(struct sql *db, struct key_def *key_def)
  return p;
 }
 
-/*
- * Both *pMem1 and *pMem2 contain string values. Compare the two values
- * using the collation sequence pColl. As usual, return a negative , zero
- * or positive value if *pMem1 is less than, equal to or greater than
- * *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
- *
- * Strungs assume to be UTF-8 encoded
- */
-static int
-vdbeCompareMemString(const Mem * pMem1, const Mem * pMem2,
-        const struct coll * pColl)
-{
- return pColl->cmp(pMem1->z, (size_t)pMem1->n,
-           pMem2->z, (size_t)pMem2->n, pColl);
-}
-
-/*
- * The input pBlob is guaranteed to be a Blob that is not marked
- * with MEM_Zero.  Return true if it could be a zero-blob.
- */
-static int
-isAllZero(const char *z, int n)
-{
- int i;
- for (i = 0; i < n; i++) {
-   if (z[i])
-     return 0;
- }
- return 1;
-}
-
-/*
- * Compare two blobs.  Return negative, zero, or positive if the first
- * is less than, equal to, or greater than the second, respectively.
- * If one blob is a prefix of the other, then the shorter is the lessor.
- */
-static SQL_NOINLINE int
-sqlBlobCompare(const Mem * pB1, const Mem * pB2)
-{
- int c;
- int n1 = pB1->n;
- int n2 = pB2->n;
-
- /* It is possible to have a Blob value that has some non-zero content
-  * followed by zero content.  But that only comes up for Blobs formed
-  * by the OP_MakeRecord opcode, and such Blobs never get passed into
-  * sqlMemCompare().
-  */
- assert((pB1->flags & MEM_Zero) == 0 || n1 == 0);
- assert((pB2->flags & MEM_Zero) == 0 || n2 == 0);
-
- if ((pB1->flags | pB2->flags) & MEM_Zero) {
-   if (pB1->flags & pB2->flags & MEM_Zero) {
-     return pB1->u.nZero - pB2->u.nZero;
-   } else if (pB1->flags & MEM_Zero) {
-     if (!isAllZero(pB2->z, pB2->n))
-       return -1;
-     return pB1->u.nZero - n2;
-   } else {
-     if (!isAllZero(pB1->z, pB1->n))
-       return +1;
-     return n1 - pB2->u.nZero;
-   }
- }
- c = memcmp(pB1->z, pB2->z, n1 > n2 ? n2 : n1);
- if (c)
-   return c;
- return n1 - n2;
-}
-
-/*
- * Compare the values contained by the two memory cells, returning
- * negative, zero or positive if pMem1 is less than, equal to, or greater
- * than pMem2. Sorting order is NULL's first, followed by numbers (integers
- * and reals) sorted numerically, followed by text ordered by the collating
- * sequence pColl and finally blob's ordered by memcmp().
- *
- * Two NULL values are considered equal by this function.
- */
-int
-sqlMemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pColl)
-{
- int f1, f2;
- int combined_flags;
-
- f1 = pMem1->flags;
- f2 = pMem2->flags;
- combined_flags = f1 | f2;
-
- /* If one value is NULL, it is less than the other. If both values
-  * are NULL, return 0.
-  */
- if (combined_flags & MEM_Null) {
-   return (f2 & MEM_Null) - (f1 & MEM_Null);
- }
-
- if ((combined_flags & MEM_Bool) != 0) {
-   if ((f1 & f2 & MEM_Bool) != 0) {
-     if (pMem1->u.b == pMem2->u.b)
-       return 0;
-     if (pMem1->u.b)
-       return 1;
-     return -1;
-   }
-   if ((f2 & MEM_Bool) != 0)
-     return +1;
-   return -1;
- }
-
- /* At least one of the two values is a number
-  */
- if ((combined_flags & (MEM_Int | MEM_UInt | MEM_Real)) != 0) {
-   if ((f1 & f2 & MEM_Int) != 0) {
-     if (pMem1->u.i < pMem2->u.i)
-       return -1;
-     if (pMem1->u.i > pMem2->u.i)
-       return +1;
-     return 0;
-   }
-   if ((f1 & f2 & MEM_UInt) != 0) {
-     if (pMem1->u.u < pMem2->u.u)
-       return -1;
-     if (pMem1->u.u > pMem2->u.u)
-       return +1;
-     return 0;
-   }
-   if ((f1 & f2 & MEM_Real) != 0) {
-     if (pMem1->u.r < pMem2->u.r)
-       return -1;
-     if (pMem1->u.r > pMem2->u.r)
-       return +1;
-     return 0;
-   }
-   if ((f1 & MEM_Int) != 0) {
-     if ((f2 & MEM_Real) != 0) {
-       return double_compare_nint64(pMem2->u.r,
-                  pMem1->u.i, -1);
-     } else {
-       return -1;
-     }
-   }
-   if ((f1 & MEM_UInt) != 0) {
-     if ((f2 & MEM_Real) != 0) {
-       return double_compare_uint64(pMem2->u.r,
-                  pMem1->u.u, -1);
-     } else if ((f2 & MEM_Int) != 0) {
-       return +1;
-     } else {
-       return -1;
-     }
-   }
-   if ((f1 & MEM_Real) != 0) {
-     if ((f2 & MEM_Int) != 0) {
-       return double_compare_nint64(pMem1->u.r,
-                  pMem2->u.i, 1);
-     } else if ((f2 & MEM_UInt) != 0) {
-       return double_compare_uint64(pMem1->u.r,
-                  pMem2->u.u, 1);
-     } else {
-       return -1;
-     }
-   }
-   return +1;
- }
-
- /* If one value is a string and the other is a blob, the string is less.
-  * If both are strings, compare using the collating functions.
-  */
- if (combined_flags & MEM_Str) {
-   if ((f1 & MEM_Str) == 0) {
-     return 1;
-   }
-   if ((f2 & MEM_Str) == 0) {
-     return -1;
-   }
-   /* The collation sequence must be defined at this point, even if
-    * the user deletes the collation sequence after the vdbe program is
-    * compiled (this was not always the case).
-    */
-   if (pColl) {
-     return vdbeCompareMemString(pMem1, pMem2, pColl);
-   } else {
-     size_t n = pMem1->n < pMem2->n ? pMem1->n : pMem2->n;
-     int res;
-     res = memcmp(pMem1->z, pMem2->z, n);
-     if (res == 0)
-       res = (int)pMem1->n - (int)pMem2->n;
-     return res;
-   }
-   /* If a NULL pointer was passed as the collate function, fall through
-    * to the blob case and use memcmp().
-    */
- }
-
- /* Both values must be blobs.  Compare using memcmp().  */
- return sqlBlobCompare(pMem1, pMem2);
-}
-
 /*
  * This routine sets the value to be returned by subsequent calls to
  * sql_changes() on the database handle 'db'.
@@ -2605,291 +2349,6 @@ sqlVdbeGetBoundValue(Vdbe * v, int iVar, u8 aff)
  return 0;
 }
 
-int
-sqlVdbeCompareMsgpack(const char **key1,
-       struct UnpackedRecord *unpacked, int key2_idx)
-{
- const char *aKey1 = *key1;
- Mem *pKey2 = unpacked->aMem + key2_idx;
- Mem mem1;
- int rc = 0;
- switch (mp_typeof(*aKey1)) {
- default:{
-     /* FIXME */
-     rc = -1;
-     break;
-   }
- case MP_NIL:{
-     rc = -((pKey2->flags & MEM_Null) == 0);
-     mp_decode_nil(&aKey1);
-     break;
-   }
- case MP_BOOL:{
-     mem1.u.b = mp_decode_bool(&aKey1);
-     if ((pKey2->flags & MEM_Bool) != 0) {
-       if (mem1.u.b != pKey2->u.b)
-         rc = mem1.u.b ? 1 : -1;
-     } else {
-       rc = (pKey2->flags & MEM_Null) != 0 ? 1 : -1;
-     }
-     break;
-   }
- case MP_UINT:{
-     mem1.u.u = mp_decode_uint(&aKey1);
-     if ((pKey2->flags & MEM_Int) != 0) {
-       rc = +1;
-     } else if ((pKey2->flags & MEM_UInt) != 0) {
-       if (mem1.u.u < pKey2->u.u)
-         rc = -1;
-       else if (mem1.u.u > pKey2->u.u)
-         rc = +1;
-     } else if ((pKey2->flags & MEM_Real) != 0) {
-       rc = double_compare_uint64(pKey2->u.r,
-                mem1.u.u, -1);
-     } else if ((pKey2->flags & MEM_Null) != 0) {
-       rc = 1;
-     } else if ((pKey2->flags & MEM_Bool) != 0) {
-       rc = 1;
-     } else {
-       rc = -1;
-     }
-     break;
-   }
- case MP_INT:{
-     mem1.u.i = mp_decode_int(&aKey1);
-     if ((pKey2->flags & MEM_UInt) != 0) {
-       rc = -1;
-     } else if ((pKey2->flags & MEM_Int) != 0) {
-       if (mem1.u.i < pKey2->u.i) {
-         rc = -1;
-       } else if (mem1.u.i > pKey2->u.i) {
-         rc = +1;
-       }
-     } else if (pKey2->flags & MEM_Real) {
-       rc = double_compare_nint64(pKey2->u.r, mem1.u.i,
-                -1);
-     } else if ((pKey2->flags & MEM_Null) != 0) {
-       rc = 1;
-     } else if ((pKey2->flags & MEM_Bool) != 0) {
-       rc = 1;
-     } else {
-       rc = -1;
-     }
-     break;
-   }
- case MP_FLOAT:{
-     mem1.u.r = mp_decode_float(&aKey1);
-     goto do_float;
-   }
- case MP_DOUBLE:{
-     mem1.u.r = mp_decode_double(&aKey1);
- do_float:
-     if ((pKey2->flags & MEM_Int) != 0) {
-       rc = double_compare_nint64(mem1.u.r, pKey2->u.i,
-                1);
-     } else if (pKey2->flags & MEM_UInt) {
-       rc = double_compare_uint64(mem1.u.r,
-                pKey2->u.u, 1);
-     } else if (pKey2->flags & MEM_Real) {
-       if (mem1.u.r < pKey2->u.r) {
-         rc = -1;
-       } else if (mem1.u.r > pKey2->u.r) {
-         rc = +1;
-       }
-     } else if ((pKey2->flags & MEM_Null) != 0) {
-       rc = 1;
-     } else if ((pKey2->flags & MEM_Bool) != 0) {
-       rc = 1;
-     } else {
-       rc = -1;
-     }
-     break;
-   }
- case MP_STR:{
-     if (pKey2->flags & MEM_Str) {
-       struct key_def *key_def = unpacked->key_def;
-       mem1.n = mp_decode_strl(&aKey1);
-       mem1.z = (char *)aKey1;
-       aKey1 += mem1.n;
-       struct coll *coll =
-         key_def->parts[key2_idx].coll;
-       if (coll != NULL) {
-         mem1.flags = MEM_Str;
-         rc = vdbeCompareMemString(&mem1, pKey2,
-                 coll);
-       } else {
-         goto do_bin_cmp;
-       }
-     } else {
-       rc = (pKey2->flags & MEM_Blob) ? -1 : +1;
-     }
-     break;
-   }
- case MP_BIN:{
-     mem1.n = mp_decode_binl(&aKey1);
-     mem1.z = (char *)aKey1;
-     aKey1 += mem1.n;
- do_blob:
-     if (pKey2->flags & MEM_Blob) {
-       if (pKey2->flags & MEM_Zero) {
-         if (!isAllZero
-             ((const char *)mem1.z, mem1.n)) {
-           rc = 1;
-         } else {
-           rc = mem1.n - pKey2->u.nZero;
-         }
-       } else {
-         int nCmp;
- do_bin_cmp:
-         nCmp = MIN(mem1.n, pKey2->n);
-         rc = memcmp(mem1.z, pKey2->z, nCmp);
-         if (rc == 0)
-           rc = mem1.n - pKey2->n;
-       }
-     } else {
-       rc = 1;
-     }
-     break;
-   }
- case MP_ARRAY:
- case MP_MAP:
- case MP_EXT:{
-     mem1.z = (char *)aKey1;
-     mp_next(&aKey1);
-     mem1.n = aKey1 - (char *)mem1.z;
-     goto do_blob;
-   }
- }
- *key1 = aKey1;
- return rc;
-}
-
-int
-sqlVdbeRecordCompareMsgpack(const void *key1,
-       struct UnpackedRecord *key2)
-{
- int rc = 0;
- u32 i, n = mp_decode_array((const char**)&key1);
-
- n = MIN(n, key2->nField);
-
- for (i = 0; i != n; i++) {
-   rc = sqlVdbeCompareMsgpack((const char**)&key1, key2, i);
-   if (rc != 0) {
-     if (key2->key_def->parts[i].sort_order !=
-         SORT_ORDER_ASC) {
-       rc = -rc;
-     }
-     return rc;
-   }
- }
-
- key2->eqSeen = 1;
- return key2->default_rc;
-}
-
-int
-vdbe_decode_msgpack_into_mem(const char *buf, struct Mem *mem, uint32_t *len)
-{
- const char *start_buf = buf;
- switch (mp_typeof(*buf)) {
- case MP_ARRAY: {
-   mem->z = (char *)buf;
-   mp_next(&buf);
-   mem->n = buf - mem->z;
-   mem->flags = MEM_Blob | MEM_Ephem | MEM_Subtype;
-   mem->subtype = SQL_SUBTYPE_MSGPACK;
-   mem->field_type = FIELD_TYPE_ARRAY;
-   break;
- }
- case MP_MAP: {
-   mem->z = (char *)buf;
-   mp_next(&buf);
-   mem->n = buf - mem->z;
-   mem->flags = MEM_Blob | MEM_Ephem | MEM_Subtype;
-   mem->subtype = SQL_SUBTYPE_MSGPACK;
-   mem->field_type = FIELD_TYPE_MAP;
-   break;
- }
- case MP_EXT: {
-   mem->z = (char *)buf;
-   mp_next(&buf);
-   mem->n = buf - mem->z;
-   mem->flags = MEM_Blob | MEM_Ephem;
-   mem->field_type = FIELD_TYPE_VARBINARY;
-   break;
- }
- case MP_NIL: {
-   mp_decode_nil(&buf);
-   mem->flags = MEM_Null;
-   mem->field_type = field_type_MAX;
-   break;
- }
- case MP_BOOL: {
-   mem->u.b = mp_decode_bool(&buf);
-   mem->flags = MEM_Bool;
-   mem->field_type = FIELD_TYPE_BOOLEAN;
-   break;
- }
- case MP_UINT: {
-   uint64_t v = mp_decode_uint(&buf);
-   mem->u.u = v;
-   mem->flags = MEM_UInt;
-   mem->field_type = FIELD_TYPE_INTEGER;
-   break;
- }
- case MP_INT: {
-   mem->u.i = mp_decode_int(&buf);
-   mem->flags = MEM_Int;
-   mem->field_type = FIELD_TYPE_INTEGER;
-   break;
- }
- case MP_STR: {
-   /* XXX u32->int */
-   mem->n = (int) mp_decode_strl(&buf);
-   mem->flags = MEM_Str | MEM_Ephem;
-   mem->field_type = FIELD_TYPE_STRING;
-install_blob:
-   mem->z = (char *)buf;
-   buf += mem->n;
-   break;
- }
- case MP_BIN: {
-   /* XXX u32->int */
-   mem->n = (int) mp_decode_binl(&buf);
-   mem->flags = MEM_Blob | MEM_Ephem;
-   mem->field_type = FIELD_TYPE_VARBINARY;
-   goto install_blob;
- }
- case MP_FLOAT: {
-   mem->u.r = mp_decode_float(&buf);
-   if (sqlIsNaN(mem->u.r)) {
-     mem->flags = MEM_Null;
-     mem->field_type = FIELD_TYPE_DOUBLE;
-   } else {
-     mem->flags = MEM_Real;
-     mem->field_type = FIELD_TYPE_DOUBLE;
-   }
-   break;
- }
- case MP_DOUBLE: {
-   mem->u.r = mp_decode_double(&buf);
-   if (sqlIsNaN(mem->u.r)) {
-     mem->flags = MEM_Null;
-     mem->field_type = FIELD_TYPE_DOUBLE;
-   } else {
-     mem->flags = MEM_Real;
-     mem->field_type = FIELD_TYPE_DOUBLE;
-   }
-   break;
- }
- default:
-   unreachable();
- }
- *len = (uint32_t)(buf - start_buf);
- return 0;
-}
-
 void
 sqlVdbeRecordUnpackMsgpack(struct key_def *key_def,  /* Information about the record format */
             const void *pKey,  /* The binary record */
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 1101f7205..092350caa 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -37,867 +37,13 @@
  * name sql_value
  */
 #include "sqlInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "tarantoolInt.h"
 #include "box/schema.h"
 #include "box/tuple.h"
 #include "mpstream/mpstream.h"
 
-#ifdef SQL_DEBUG
-/*
- * Check invariants on a Mem object.
- *
- * This routine is intended for use inside of assert() statements, like
- * this:    assert( sqlVdbeCheckMemInvariants(pMem) );
- */
-int
-sqlVdbeCheckMemInvariants(Mem * p)
-{
- /* If MEM_Dyn is set then Mem.xDel!=0.
-  * Mem.xDel is might not be initialized if MEM_Dyn is clear.
-  */
- assert((p->flags & MEM_Dyn) == 0 || p->xDel != 0);
-
- /* MEM_Dyn may only be set if Mem.szMalloc==0.  In this way we
-  * ensure that if Mem.szMalloc>0 then it is safe to do
-  * Mem.z = Mem.zMalloc without having to check Mem.flags&MEM_Dyn.
-  * That saves a few cycles in inner loops.
-  */
- assert((p->flags & MEM_Dyn) == 0 || p->szMalloc == 0);
-
- /* Cannot be both MEM_Int and MEM_Real at the same time */
- assert((p->flags & (MEM_Int | MEM_Real)) != (MEM_Int | MEM_Real));
- /* Can't be both UInt and Int at the same time.  */
- assert((p->flags & (MEM_Int | MEM_UInt)) != (MEM_Int | MEM_UInt));
-
- /* The szMalloc field holds the correct memory allocation size */
- assert(p->szMalloc == 0 ||
-        p->szMalloc == sqlDbMallocSize(p->db, p->zMalloc));
-
- /* If p holds a string or blob, the Mem.z must point to exactly
-  * one of the following:
-  *
-  *   (1) Memory in Mem.zMalloc and managed by the Mem object
-  *   (2) Memory to be freed using Mem.xDel
-  *   (3) An ephemeral string or blob
-  *   (4) A static string or blob
-  */
- if ((p->flags & (MEM_Str | MEM_Blob)) && p->n > 0) {
-   assert(((p->szMalloc > 0 && p->z == p->zMalloc) ? 1 : 0) +
-          ((p->flags & MEM_Dyn) != 0 ? 1 : 0) +
-          ((p->flags & MEM_Ephem) != 0 ? 1 : 0) +
-          ((p->flags & MEM_Static) != 0 ? 1 : 0) == 1);
- }
- return 1;
-}
-#endif
-
-/*
- * Make sure pMem->z points to a writable allocation of at least
- * min(n,32) bytes.
- *
- * If the bPreserve argument is true, then copy of the content of
- * pMem->z into the new allocation.  pMem must be either a string or
- * blob if bPreserve is true.  If bPreserve is false, any prior content
- * in pMem->z is discarded.
- */
-SQL_NOINLINE int
-sqlVdbeMemGrow(Mem * pMem, int n, int bPreserve)
-{
- assert(sqlVdbeCheckMemInvariants(pMem));
- testcase(pMem->db == 0);
-
- /* If the bPreserve flag is set to true, then the memory cell must already
-  * contain a valid string or blob value.
-  */
- assert(bPreserve == 0 || pMem->flags & (MEM_Blob | MEM_Str));
- testcase(bPreserve && pMem->z == 0);
-
- assert(pMem->szMalloc == 0 ||
-        pMem->szMalloc == sqlDbMallocSize(pMem->db, pMem->zMalloc));
- if (pMem->szMalloc < n) {
-   if (n < 32)
-     n = 32;
-   if (bPreserve && pMem->szMalloc > 0 && pMem->z == pMem->zMalloc) {
-     pMem->z = pMem->zMalloc =
-         sqlDbReallocOrFree(pMem->db, pMem->z, n);
-     bPreserve = 0;
-   } else {
-     if (pMem->szMalloc > 0)
-       sqlDbFree(pMem->db, pMem->zMalloc);
-     pMem->zMalloc = sqlDbMallocRaw(pMem->db, n);
-   }
-   if (pMem->zMalloc == 0) {
-     sqlVdbeMemSetNull(pMem);
-     pMem->z = 0;
-     pMem->szMalloc = 0;
-     return -1;
-   } else {
-     pMem->szMalloc = sqlDbMallocSize(pMem->db,
-              pMem->zMalloc);
-   }
- }
-
- if (bPreserve && pMem->z && pMem->z != pMem->zMalloc) {
-   memcpy(pMem->zMalloc, pMem->z, pMem->n);
- }
- if ((pMem->flags & MEM_Dyn) != 0) {
-   assert(pMem->xDel != 0 && pMem->xDel != SQL_DYNAMIC);
-   pMem->xDel((void *)(pMem->z));
- }
-
- pMem->z = pMem->zMalloc;
- pMem->flags &= ~(MEM_Dyn | MEM_Ephem | MEM_Static);
- return 0;
-}
-
-/*
- * Change the pMem->zMalloc allocation to be at least szNew bytes.
- * If pMem->zMalloc already meets or exceeds the requested size, this
- * routine is a no-op.
- *
- * Any prior string or blob content in the pMem object may be discarded.
- * The pMem->xDel destructor is called, if it exists.  Though MEM_Str
- * and MEM_Blob values may be discarded, MEM_Int, MEM_Real, and MEM_Null
- * values are preserved.
- *
- * Return 0 on success or -1 if unable to complete the resizing.
- */
-int
-sqlVdbeMemClearAndResize(Mem * pMem, int szNew)
-{
- assert(szNew > 0);
- assert((pMem->flags & MEM_Dyn) == 0 || pMem->szMalloc == 0);
- if (pMem->szMalloc < szNew) {
-   return sqlVdbeMemGrow(pMem, szNew, 0);
- }
- assert((pMem->flags & MEM_Dyn) == 0);
- pMem->z = pMem->zMalloc;
- pMem->flags &= (MEM_Null | MEM_Int | MEM_Real);
- return 0;
-}
-
-/*
- * Change pMem so that its MEM_Str or MEM_Blob value is stored in
- * MEM.zMalloc, where it can be safely written.
- *
- * Return 0 on success or -1 if malloc fails.
- */
-int
-sqlVdbeMemMakeWriteable(Mem * pMem)
-{
- if ((pMem->flags & (MEM_Str | MEM_Blob)) != 0) {
-   if (ExpandBlob(pMem))
-     return -1;
-   if (pMem->szMalloc == 0 || pMem->z != pMem->zMalloc) {
-     if (sqlVdbeMemGrow(pMem, pMem->n + 2, 1)) {
-       return -1;
-     }
-     pMem->z[pMem->n] = 0;
-     pMem->z[pMem->n + 1] = 0;
-     pMem->flags |= MEM_Term;
-   }
- }
- pMem->flags &= ~MEM_Ephem;
-#ifdef SQL_DEBUG
- pMem->pScopyFrom = 0;
-#endif
-
- return 0;
-}
-
-/*
- * If the given Mem* has a zero-filled tail, turn it into an ordinary
- * blob stored in dynamically allocated space.
- */
-int
-sqlVdbeMemExpandBlob(Mem * pMem)
-{
- int nByte;
- assert(pMem->flags & MEM_Zero);
- assert(pMem->flags & MEM_Blob);
-
- /* Set nByte to the number of bytes required to store the expanded blob. */
- nByte = pMem->n + pMem->u.nZero;
- if (nByte <= 0) {
-   nByte = 1;
- }
- if (sqlVdbeMemGrow(pMem, nByte, 1)) {
-   return -1;
- }
-
- memset(&pMem->z[pMem->n], 0, pMem->u.nZero);
- pMem->n += pMem->u.nZero;
- pMem->flags &= ~(MEM_Zero | MEM_Term);
- return 0;
-}
-
-/*
- * It is already known that pMem contains an unterminated string.
- * Add the zero terminator.
- */
-static SQL_NOINLINE int
-vdbeMemAddTerminator(Mem * pMem)
-{
- if (sqlVdbeMemGrow(pMem, pMem->n + 2, 1)) {
-   return -1;
- }
- pMem->z[pMem->n] = 0;
- pMem->z[pMem->n + 1] = 0;
- pMem->flags |= MEM_Term;
- return 0;
-}
-
-/*
- * Make sure the given Mem is \u0000 terminated.
- */
-int
-sqlVdbeMemNulTerminate(Mem * pMem)
-{
- testcase((pMem->flags & (MEM_Term | MEM_Str)) == (MEM_Term | MEM_Str));
- testcase((pMem->flags & (MEM_Term | MEM_Str)) == 0);
- if ((pMem->flags & (MEM_Term | MEM_Str)) != MEM_Str) {
-   return 0; /* Nothing to do */
- } else {
-   return vdbeMemAddTerminator(pMem);
- }
-}
-
-static inline bool
-mem_has_msgpack_subtype(struct Mem *mem)
-{
- return (mem->flags & MEM_Subtype) != 0 &&
-        mem->subtype == SQL_SUBTYPE_MSGPACK;
-}
-
-/*
- * Add MEM_Str to the set of representations for the given Mem.  Numbers
- * are converted using sql_snprintf().  Converting a BLOB to a string
- * is a no-op.
- *
- * Existing representations MEM_Int and MEM_Real are invalidated if
- * bForce is true but are retained if bForce is false.
- *
- * A MEM_Null value will never be passed to this function. This function is
- * used for converting values to text for returning to the user (i.e. via
- * sql_value_text()), or for ensuring that values to be used as btree
- * keys are strings. In the former case a NULL pointer is returned the
- * user and the latter is an internal programming error.
- */
-int
-sqlVdbeMemStringify(Mem * pMem)
-{
- int fg = pMem->flags;
- int nByte = 32;
-
- if ((fg & (MEM_Null | MEM_Str | MEM_Blob)) != 0 &&
-     !mem_has_msgpack_subtype(pMem))
-   return 0;
-
- assert(!(fg & MEM_Zero));
- assert((fg & (MEM_Int | MEM_UInt | MEM_Real | MEM_Bool |
-         MEM_Blob)) != 0);
- assert(EIGHT_BYTE_ALIGNMENT(pMem));
-
- /*
-  * In case we have ARRAY/MAP we should save decoded value
-  * before clearing pMem->z.
-  */
- char *value = NULL;
- if (mem_has_msgpack_subtype(pMem)) {
-   const char *value_str = mp_str(pMem->z);
-   nByte = strlen(value_str) + 1;
-   value = region_alloc(&fiber()->gc, nByte);
-   memcpy(value, value_str, nByte);
- }
-
- if (sqlVdbeMemClearAndResize(pMem, nByte)) {
-   return -1;
- }
- if (fg & MEM_Int) {
-   sql_snprintf(nByte, pMem->z, "%lld", pMem->u.i);
-   pMem->flags &= ~MEM_Int;
- } else if ((fg & MEM_UInt) != 0) {
-   sql_snprintf(nByte, pMem->z, "%llu", pMem->u.u);
-   pMem->flags &= ~MEM_UInt;
- } else if ((fg & MEM_Bool) != 0) {
-   sql_snprintf(nByte, pMem->z, "%s",
-          SQL_TOKEN_BOOLEAN(pMem->u.b));
-   pMem->flags &= ~MEM_Bool;
- } else if (mem_has_msgpack_subtype(pMem)) {
-   sql_snprintf(nByte, pMem->z, "%s", value);
-   pMem->flags &= ~MEM_Subtype;
-   pMem->subtype = SQL_SUBTYPE_NO;
- } else {
-   assert(fg & MEM_Real);
-   sql_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
-   pMem->flags &= ~MEM_Real;
- }
- pMem->n = sqlStrlen30(pMem->z);
- pMem->flags |= MEM_Str | MEM_Term;
- return 0;
-}
-
-int
-sql_vdbemem_finalize(struct Mem *mem, struct func *func)
-{
- assert(func != NULL);
- assert(func->def->language == FUNC_LANGUAGE_SQL_BUILTIN);
- assert(func->def->aggregate == FUNC_AGGREGATE_GROUP);
- assert((mem->flags & MEM_Null) != 0 || func == mem->u.func);
- sql_context ctx;
- memset(&ctx, 0, sizeof(ctx));
- Mem t;
- memset(&t, 0, sizeof(t));
- t.flags = MEM_Null;
- t.db = mem->db;
- t.field_type = field_type_MAX;
- ctx.pOut = &t;
- ctx.pMem = mem;
- ctx.func = func;
- ((struct func_sql_builtin *)func)->finalize(&ctx);
- assert((mem->flags & MEM_Dyn) == 0);
- if (mem->szMalloc > 0)
-   sqlDbFree(mem->db, mem->zMalloc);
- memcpy(mem, &t, sizeof(t));
- return ctx.is_aborted ? -1 : 0;
-}
-
-/*
- * If the memory cell contains a value that must be freed by
- * invoking the external callback in Mem.xDel, then this routine
- * will free that value.  It also sets Mem.flags to MEM_Null.
- *
- * This is a helper routine for sqlVdbeMemSetNull() and
- * for sqlVdbeMemRelease().  Use those other routines as the
- * entry point for releasing Mem resources.
- */
-static SQL_NOINLINE void
-vdbeMemClearExternAndSetNull(Mem * p)
-{
- assert(VdbeMemDynamic(p));
- if (p->flags & MEM_Agg) {
-   sql_vdbemem_finalize(p, p->u.func);
-   assert((p->flags & MEM_Agg) == 0);
-   testcase(p->flags & MEM_Dyn);
- }
- if (p->flags & MEM_Dyn) {
-   assert(p->xDel != SQL_DYNAMIC && p->xDel != 0);
-   p->xDel((void *)p->z);
- } else if (p->flags & MEM_Frame) {
-   VdbeFrame *pFrame = p->u.pFrame;
-   pFrame->pParent = pFrame->v->pDelFrame;
-   pFrame->v->pDelFrame = pFrame;
- }
- p->flags = MEM_Null;
-}
-
-/*
- * Release memory held by the Mem p, both external memory cleared
- * by p->xDel and memory in p->zMalloc.
- *
- * This is a helper routine invoked by sqlVdbeMemRelease() in
- * the unusual case where there really is memory in p that needs
- * to be freed.
- */
-static SQL_NOINLINE void
-vdbeMemClear(Mem * p)
-{
- if (VdbeMemDynamic(p)) {
-   vdbeMemClearExternAndSetNull(p);
- }
- if (p->szMalloc) {
-   sqlDbFree(p->db, p->zMalloc);
-   p->szMalloc = 0;
- }
- p->z = 0;
-}
-
-/*
- * Release any memory resources held by the Mem.  Both the memory that is
- * free by Mem.xDel and the Mem.zMalloc allocation are freed.
- *
- * Use this routine prior to clean up prior to abandoning a Mem, or to
- * reset a Mem back to its minimum memory utilization.
- *
- * Use sqlVdbeMemSetNull() to release just the Mem.xDel space
- * prior to inserting new content into the Mem.
- */
-void
-sqlVdbeMemRelease(Mem * p)
-{
- assert(sqlVdbeCheckMemInvariants(p));
- if (VdbeMemDynamic(p) || p->szMalloc) {
-   vdbeMemClear(p);
- }
-}
-
-enum mp_type
-mem_mp_type(struct Mem *mem)
-{
- switch (mem->flags & MEM_PURE_TYPE_MASK) {
- case MEM_Int:
-   return MP_INT;
- case MEM_UInt:
-   return MP_UINT;
- case MEM_Real:
-   return MP_DOUBLE;
- case MEM_Str:
-   return MP_STR;
- case MEM_Blob:
-   if ((mem->flags & MEM_Subtype) == 0 ||
-        mem->subtype != SQL_SUBTYPE_MSGPACK)
-     return MP_BIN;
-   assert(mp_typeof(*mem->z) == MP_MAP ||
-          mp_typeof(*mem->z) == MP_ARRAY);
-   return mp_typeof(*mem->z);
- case MEM_Bool:
-   return MP_BOOL;
- case MEM_Null:
-   return MP_NIL;
- default: unreachable();
- }
-}
-
-/*
- * Convert a 64-bit IEEE double into a 64-bit signed integer.
- * If the double is out of range of a 64-bit signed integer then
- * return the closest available 64-bit signed integer.
- */
-static int
-doubleToInt64(double r, int64_t *i)
-{
- /*
-  * Many compilers we encounter do not define constants for the
-  * minimum and maximum 64-bit integers, or they define them
-  * inconsistently.  And many do not understand the "LL" notation.
-  * So we define our own static constants here using nothing
-  * larger than a 32-bit integer constant.
-  */
- static const int64_t maxInt = LARGEST_INT64;
- static const int64_t minInt = SMALLEST_INT64;
- if (r <= (double)minInt) {
-   *i = minInt;
-   return -1;
- } else if (r >= (double)maxInt) {
-   *i = maxInt;
-   return -1;
- } else {
-   *i = (int64_t) r;
-   return *i != r;
- }
-}
-
-/*
- * Return some kind of integer value which is the best we can do
- * at representing the value that *pMem describes as an integer.
- * If pMem is an integer, then the value is exact.  If pMem is
- * a floating-point then the value returned is the integer part.
- * If pMem is a string or blob, then we make an attempt to convert
- * it into an integer and return that.  If pMem represents an
- * an SQL-NULL value, return 0.
- *
- * If pMem represents a string value, its encoding might be changed.
- */
-int
-sqlVdbeIntValue(Mem * pMem, int64_t *i, bool *is_neg)
-{
- int flags;
- assert(EIGHT_BYTE_ALIGNMENT(pMem));
- flags = pMem->flags;
- if (flags & MEM_Int) {
-   *i = pMem->u.i;
-   *is_neg = true;
-   return 0;
- } else if (flags & MEM_UInt) {
-   *i = pMem->u.u;
-   *is_neg = false;
-   return 0;
- } else if (flags & MEM_Real) {
-   *is_neg = pMem->u.r < 0;
-   return doubleToInt64(pMem->u.r, i);
- } else if (flags & (MEM_Str)) {
-   assert(pMem->z || pMem->n == 0);
-   if (sql_atoi64(pMem->z, i, is_neg, pMem->n) == 0)
-     return 0;
- }
- return -1;
-}
-
-/*
- * Return the best representation of pMem that we can get into a
- * double.  If pMem is already a double or an integer, return its
- * value.  If it is a string or blob, try to convert it to a double.
- * If it is a NULL, return 0.0.
- */
-int
-sqlVdbeRealValue(Mem * pMem, double *v)
-{
- assert(EIGHT_BYTE_ALIGNMENT(pMem));
- if (pMem->flags & MEM_Real) {
-   *v = pMem->u.r;
-   return 0;
- } else if (pMem->flags & MEM_Int) {
-   *v = (double)pMem->u.i;
-   return 0;
- } else if ((pMem->flags & MEM_UInt) != 0) {
-   *v = (double)pMem->u.u;
-   return 0;
- } else if (pMem->flags & MEM_Str) {
-   if (sqlAtoF(pMem->z, v, pMem->n))
-     return 0;
- }
- return -1;
-}
-
-int
-mem_value_bool(const struct Mem *mem, bool *b)
-{
- if ((mem->flags  & MEM_Bool) != 0) {
-   *b = mem->u.b;
-   return 0;
- }
- return -1;
-}
-
-/*
- * The MEM structure is already a MEM_Real.  Try to also make it a
- * MEM_Int if we can.
- */
-int
-mem_apply_integer_type(Mem *pMem)
-{
- int rc;
- i64 ix;
- assert(pMem->flags & MEM_Real);
- assert(EIGHT_BYTE_ALIGNMENT(pMem));
-
- if ((rc = doubleToInt64(pMem->u.r, (int64_t *) &ix)) == 0)
-   mem_set_int(pMem, ix, pMem->u.r <= -1);
- return rc;
-}
-
-/*
- * Convert pMem so that it is of type MEM_Real.
- * Invalidate any prior representations.
- */
-int
-sqlVdbeMemRealify(Mem * pMem)
-{
- assert(EIGHT_BYTE_ALIGNMENT(pMem));
- double v;
- if (sqlVdbeRealValue(pMem, &v))
-   return -1;
- mem_set_double(pMem, v);
- return 0;
-}
-
-int
-vdbe_mem_numerify(struct Mem *mem)
-{
- if ((mem->flags & (MEM_Int | MEM_UInt | MEM_Real | MEM_Null)) != 0)
-   return 0;
- if ((mem->flags & MEM_Bool) != 0) {
-   mem->u.u = mem->u.b;
-   MemSetTypeFlag(mem, MEM_UInt);
-   return 0;
- }
- assert((mem->flags & (MEM_Blob | MEM_Str)) != 0);
- bool is_neg;
- int64_t i;
- if (sql_atoi64(mem->z, &i, &is_neg, mem->n) == 0) {
-   mem_set_int(mem, i, is_neg);
- } else {
-   double d;
-   if (sqlAtoF(mem->z, &d, mem->n) == 0)
-     return -1;
-   mem_set_double(mem, d);
- }
- return 0;
-}
-
-/**
- * According to ANSI SQL string value can be converted to boolean
- * type if string consists of literal "true" or "false" and
- * number of leading and trailing spaces.
- *
- * For instance, "   tRuE  " can be successfully converted to
- * boolean value true.
- *
- * @param str String to be converted to boolean. Assumed to be
- *        null terminated.
- * @param[out] result Resulting value of cast.
- * @retval 0 If string satisfies conditions above.
- * @retval -1 Otherwise.
- */
-static int
-str_cast_to_boolean(const char *str, bool *result)
-{
- assert(str != NULL);
- for (; *str == ' '; str++);
- if (strncasecmp(str, SQL_TOKEN_TRUE, strlen(SQL_TOKEN_TRUE)) == 0) {
-   *result = true;
-   str += 4;
- } else if (strncasecmp(str, SQL_TOKEN_FALSE,
-            strlen(SQL_TOKEN_FALSE)) == 0) {
-   *result = false;
-   str += 5;
- } else {
-   return -1;
- }
- for (; *str != '\0'; ++str) {
-   if (*str != ' ')
-     return -1;
- }
- return 0;
-}
-
-/*
- * Cast the datatype of the value in pMem according to the type
- * @type.  Casting is different from applying type in that a cast
- * is forced.  In other words, the value is converted into the desired
- * type even if that results in loss of data.  This routine is
- * used (for example) to implement the SQL "cast()" operator.
- */
-int
-sqlVdbeMemCast(Mem * pMem, enum field_type type)
-{
- assert(type < field_type_MAX);
- if (pMem->flags & MEM_Null)
-   return 0;
- switch (type) {
- case FIELD_TYPE_SCALAR:
-   return 0;
- case FIELD_TYPE_BOOLEAN:
-   if ((pMem->flags & MEM_Int) != 0) {
-     mem_set_bool(pMem, pMem->u.i);
-     return 0;
-   }
-   if ((pMem->flags & MEM_UInt) != 0) {
-     mem_set_bool(pMem, pMem->u.u);
-     return 0;
-   }
-   if ((pMem->flags & MEM_Real) != 0) {
-     mem_set_bool(pMem, pMem->u.r);
-     return 0;
-   }
-   if ((pMem->flags & MEM_Str) != 0) {
-     bool value;
-     if (str_cast_to_boolean(pMem->z, &value) != 0)
-       return -1;
-     mem_set_bool(pMem, value);
-     return 0;
-   }
-   if ((pMem->flags & MEM_Bool) != 0)
-     return 0;
-   return -1;
- case FIELD_TYPE_INTEGER:
- case FIELD_TYPE_UNSIGNED:
-   if ((pMem->flags & (MEM_Blob | MEM_Str)) != 0) {
-     bool is_neg;
-     int64_t val;
-     if (sql_atoi64(pMem->z, &val, &is_neg, pMem->n) != 0)
-       return -1;
-     if (type == FIELD_TYPE_UNSIGNED && is_neg)
-       return -1;
-     mem_set_int(pMem, val, is_neg);
-     return 0;
-   }
-   if ((pMem->flags & MEM_Bool) != 0) {
-     pMem->u.u = pMem->u.b;
-     MemSetTypeFlag(pMem, MEM_UInt);
-     return 0;
-   }
-   if ((pMem->flags & MEM_Real) != 0) {
-     double d;
-     if (sqlVdbeRealValue(pMem, &d) != 0)
-       return -1;
-     if (d < (double)INT64_MAX && d >= (double)INT64_MIN) {
-       mem_set_int(pMem, d, d <= -1);
-       return 0;
-     }
-     if (d >= (double)INT64_MAX && d < (double)UINT64_MAX) {
-       mem_set_u64(pMem, d);
-       return 0;
-     }
-     return -1;
-   }
-   if (type == FIELD_TYPE_UNSIGNED &&
-       (pMem->flags & MEM_UInt) == 0)
-     return -1;
-   return 0;
- case FIELD_TYPE_DOUBLE:
-   return sqlVdbeMemRealify(pMem);
- case FIELD_TYPE_NUMBER:
-   return vdbe_mem_numerify(pMem);
- case FIELD_TYPE_VARBINARY:
-   if ((pMem->flags & MEM_Blob) != 0)
-     return 0;
-   if ((pMem->flags & MEM_Str) != 0) {
-     MemSetTypeFlag(pMem, MEM_Str);
-     return 0;
-   }
-   return -1;
- default:
-   assert(type == FIELD_TYPE_STRING);
-   assert(MEM_Str == (MEM_Blob >> 3));
-   if ((pMem->flags & MEM_Bool) != 0) {
-     const char *str_bool = SQL_TOKEN_BOOLEAN(pMem->u.b);
-     sqlVdbeMemSetStr(pMem, str_bool, strlen(str_bool), 1,
-          SQL_TRANSIENT);
-     return 0;
-   }
-   pMem->flags |= (pMem->flags & MEM_Blob) >> 3;
-     sql_value_apply_type(pMem, FIELD_TYPE_STRING);
-   assert(pMem->flags & MEM_Str || pMem->db->mallocFailed);
-   pMem->flags &=
-     ~(MEM_Int | MEM_UInt | MEM_Real | MEM_Blob | MEM_Zero);
-   return 0;
- }
-}
-
-/*
- * Initialize bulk memory to be a consistent Mem object.
- *
- * The minimum amount of initialization feasible is performed.
- */
-void
-sqlVdbeMemInit(Mem * pMem, sql * db, u32 flags)
-{
- assert((flags & ~MEM_TypeMask) == 0);
- pMem->flags = flags;
- pMem->db = db;
- pMem->szMalloc = 0;
- pMem->field_type = field_type_MAX;
-}
-
-/*
- * Delete any previous value and set the value stored in *pMem to NULL.
- *
- * This routine calls the Mem.xDel destructor to dispose of values that
- * require the destructor.  But it preserves the Mem.zMalloc memory allocation.
- * To free all resources, use sqlVdbeMemRelease(), which both calls this
- * routine to invoke the destructor and deallocates Mem.zMalloc.
- *
- * Use this routine to reset the Mem prior to insert a new value.
- *
- * Use sqlVdbeMemRelease() to complete erase the Mem prior to abandoning it.
- */
-void
-sqlVdbeMemSetNull(Mem * pMem)
-{
- if (VdbeMemDynamic(pMem)) {
-   vdbeMemClearExternAndSetNull(pMem);
- } else {
-   pMem->flags = MEM_Null;
- }
-}
-
-void
-sqlValueSetNull(sql_value * p)
-{
- sqlVdbeMemSetNull((Mem *) p);
-}
-
-void
-mem_set_ptr(struct Mem *mem, void *ptr)
-{
- sqlVdbeMemRelease(mem);
- mem->flags = MEM_Ptr;
- mem->u.p = ptr;
-}
-
-/*
- * Delete any previous value and set the value to be a BLOB of length
- * n containing all zeros.
- */
-void
-sqlVdbeMemSetZeroBlob(Mem * pMem, int n)
-{
- sqlVdbeMemRelease(pMem);
- pMem->flags = MEM_Blob | MEM_Zero;
- pMem->n = 0;
- if (n < 0)
-   n = 0;
- pMem->u.nZero = n;
- pMem->z = 0;
-}
-
-void
-mem_set_bool(struct Mem *mem, bool value)
-{
- sqlVdbeMemSetNull(mem);
- mem->u.b = value;
- mem->flags = MEM_Bool;
- mem->field_type = FIELD_TYPE_BOOLEAN;
-}
-
-void
-mem_set_i64(struct Mem *mem, int64_t value)
-{
- if (VdbeMemDynamic(mem))
-   sqlVdbeMemSetNull(mem);
- mem->u.i = value;
- int flag = value < 0 ? MEM_Int : MEM_UInt;
- MemSetTypeFlag(mem, flag);
- mem->field_type = FIELD_TYPE_INTEGER;
-}
-
-void
-mem_set_u64(struct Mem *mem, uint64_t value)
-{
- if (VdbeMemDynamic(mem))
-   sqlVdbeMemSetNull(mem);
- mem->u.u = value;
- MemSetTypeFlag(mem, MEM_UInt);
- mem->field_type = FIELD_TYPE_UNSIGNED;
-}
-
-void
-mem_set_int(struct Mem *mem, int64_t value, bool is_neg)
-{
- if (VdbeMemDynamic(mem))
-   sqlVdbeMemSetNull(mem);
- if (is_neg) {
-   assert(value < 0);
-   mem->u.i = value;
-   MemSetTypeFlag(mem, MEM_Int);
- } else {
-   mem->u.u = value;
-   MemSetTypeFlag(mem, MEM_UInt);
- }
- mem->field_type = FIELD_TYPE_INTEGER;
-}
-
-void
-mem_set_double(struct Mem *mem, double value)
-{
- sqlVdbeMemSetNull(mem);
- if (sqlIsNaN(value))
-   return;
- mem->u.r = value;
- MemSetTypeFlag(mem, MEM_Real);
- mem->field_type = FIELD_TYPE_DOUBLE;
-}
-
-/*
- * Return true if the Mem object contains a TEXT or BLOB that is
- * too large - whose size exceeds SQL_MAX_LENGTH.
- */
-int
-sqlVdbeMemTooBig(Mem * p)
-{
- assert(p->db != 0);
- if (p->flags & (MEM_Str | MEM_Blob)) {
-   int n = p->n;
-   if (p->flags & MEM_Zero) {
-     n += p->u.nZero;
-   }
-   return n > p->db->aLimit[SQL_LIMIT_LENGTH];
- }
- return 0;
-}
-
 #ifdef SQL_DEBUG
 /*
  * This routine prepares a memory cell for modification by breaking
@@ -921,173 +67,6 @@ sqlVdbeMemAboutToChange(Vdbe * pVdbe, Mem * pMem)
  pMem->pScopyFrom = 0;
 }
 #endif       /* SQL_DEBUG */
-
-/*
- * Make an shallow copy of pFrom into pTo.  Prior contents of
- * pTo are freed.  The pFrom->z field is not duplicated.  If
- * pFrom->z is used, then pTo->z points to the same thing as pFrom->z
- * and flags gets srcType (either MEM_Ephem or MEM_Static).
- */
-static SQL_NOINLINE void
-vdbeClrCopy(Mem * pTo, const Mem * pFrom, int eType)
-{
- vdbeMemClearExternAndSetNull(pTo);
- assert(!VdbeMemDynamic(pTo));
- sqlVdbeMemShallowCopy(pTo, pFrom, eType);
-}
-
-void
-sqlVdbeMemShallowCopy(Mem * pTo, const Mem * pFrom, int srcType)
-{
- assert(pTo->db == pFrom->db);
- if (VdbeMemDynamic(pTo)) {
-   vdbeClrCopy(pTo, pFrom, srcType);
-   return;
- }
- memcpy(pTo, pFrom, MEMCELLSIZE);
- if ((pFrom->flags & MEM_Static) == 0) {
-   pTo->flags &= ~(MEM_Dyn | MEM_Static | MEM_Ephem);
-   assert(srcType == MEM_Ephem || srcType == MEM_Static);
-   pTo->flags |= srcType;
- }
-}
-
-/*
- * Make a full copy of pFrom into pTo.  Prior contents of pTo are
- * freed before the copy is made.
- */
-int
-sqlVdbeMemCopy(Mem * pTo, const Mem * pFrom)
-{
- int rc = 0;
-
- if (VdbeMemDynamic(pTo))
-   vdbeMemClearExternAndSetNull(pTo);
- memcpy(pTo, pFrom, MEMCELLSIZE);
- pTo->flags &= ~MEM_Dyn;
- if (pTo->flags & (MEM_Str | MEM_Blob)) {
-   if (0 == (pFrom->flags & MEM_Static)) {
-     pTo->flags |= MEM_Ephem;
-     rc = sqlVdbeMemMakeWriteable(pTo);
-   }
- }
-
- return rc;
-}
-
-/*
- * Transfer the contents of pFrom to pTo. Any existing value in pTo is
- * freed. If pFrom contains ephemeral data, a copy is made.
- *
- * pFrom contains an SQL NULL when this routine returns.
- */
-void
-sqlVdbeMemMove(Mem * pTo, Mem * pFrom)
-{
- assert(pFrom->db == 0 || pTo->db == 0 || pFrom->db == pTo->db);
-
- sqlVdbeMemRelease(pTo);
- memcpy(pTo, pFrom, sizeof(Mem));
- pFrom->flags = MEM_Null;
- pFrom->szMalloc = 0;
-}
-
-/*
- * Change the value of a Mem to be a string or a BLOB.
- *
- * The memory management strategy depends on the value of the xDel
- * parameter. If the value passed is SQL_TRANSIENT, then the
- * string is copied into a (possibly existing) buffer managed by the
- * Mem structure. Otherwise, any existing buffer is freed and the
- * pointer copied.
- *
- * If the string is too large (if it exceeds the SQL_LIMIT_LENGTH
- * size limit) then no memory allocation occurs.  If the string can be
- * stored without allocating memory, then it is.  If a memory allocation
- * is required to store the string, then value of pMem is unchanged.  In
- * either case, error is returned.
- */
-int
-sqlVdbeMemSetStr(Mem * pMem, /* Memory cell to set to string value */
-        const char *z, /* String pointer */
-        int n, /* Bytes in string, or negative */
-        u8 not_blob, /* Encoding of z.  0 for BLOBs */
-        void (*xDel) (void *)  /* Destructor function */
-    )
-{
- int nByte = n;    /* New value for pMem->n */
- int iLimit;   /* Maximum allowed string or blob size */
- u16 flags = 0;    /* New value for pMem->flags */
-
- /* If z is a NULL pointer, set pMem to contain an SQL NULL. */
- if (!z) {
-   sqlVdbeMemSetNull(pMem);
-   return 0;
- }
-
- if (pMem->db) {
-   iLimit = pMem->db->aLimit[SQL_LIMIT_LENGTH];
- } else {
-   iLimit = SQL_MAX_LENGTH;
- }
- flags = (not_blob == 0 ? MEM_Blob : MEM_Str);
- if (nByte < 0) {
-   assert(not_blob != 0);
-   nByte = sqlStrlen30(z);
-   if (nByte > iLimit)
-     nByte = iLimit + 1;
-   flags |= MEM_Term;
- }
-
- /* The following block sets the new values of Mem.z and Mem.xDel. It
-  * also sets a flag in local variable "flags" to indicate the memory
-  * management (one of MEM_Dyn or MEM_Static).
-  */
- if (xDel == SQL_TRANSIENT) {
-   int nAlloc = nByte;
-   if (flags & MEM_Term) {
-     nAlloc += 1; //SQL_UTF8
-   }
-   if (nByte > iLimit) {
-     diag_set(ClientError, ER_SQL_EXECUTE, "string or binary"\
-        "string is too big");
-     return -1;
-   }
-   testcase(nAlloc == 0);
-   testcase(nAlloc == 31);
-   testcase(nAlloc == 32);
-   if (sqlVdbeMemClearAndResize(pMem, MAX(nAlloc, 32))) {
-     return -1;
-   }
-   memcpy(pMem->z, z, nAlloc);
- } else if (xDel == SQL_DYNAMIC) {
-   sqlVdbeMemRelease(pMem);
-   pMem->zMalloc = pMem->z = (char *)z;
-   pMem->szMalloc = sqlDbMallocSize(pMem->db, pMem->zMalloc);
- } else {
-   sqlVdbeMemRelease(pMem);
-   pMem->z = (char *)z;
-   pMem->xDel = xDel;
-   flags |= ((xDel == SQL_STATIC) ? MEM_Static : MEM_Dyn);
- }
-
- pMem->n = nByte;
- pMem->flags = flags;
- assert((pMem->flags & (MEM_Str | MEM_Blob)) != 0);
- if ((pMem->flags & MEM_Str) != 0)
-   pMem->field_type = FIELD_TYPE_STRING;
- else
-   pMem->field_type = FIELD_TYPE_VARBINARY;
-
- if (nByte > iLimit) {
-   diag_set(ClientError, ER_SQL_EXECUTE, "string or binary string"\
-      "is too big");
-   return -1;
- }
-
- return 0;
-}
-
 /*
  * Move data out of a btree key or data field and into a Mem structure.
  * The data is payload from the entry that pCur is currently pointing
@@ -1153,79 +132,6 @@ sqlVdbeMemFromBtree(BtCursor * pCur,  /* Cursor pointing at record to retrieve. *
  return rc;
 }
 
-/*
- * The pVal argument is known to be a value other than NULL.
- * Convert it into a string with encoding enc and return a pointer
- * to a zero-terminated version of that string.
- */
-static SQL_NOINLINE const void *
-valueToText(sql_value * pVal)
-{
- assert(pVal != 0);
- assert((pVal->flags & (MEM_Null)) == 0);
- if ((pVal->flags & (MEM_Blob | MEM_Str)) &&
-     !mem_has_msgpack_subtype(pVal)) {
-   if (ExpandBlob(pVal))
-     return 0;
-   pVal->flags |= MEM_Str;
-   sqlVdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */
- } else {
-   sqlVdbeMemStringify(pVal);
-   assert(0 == (1 & SQL_PTR_TO_INT(pVal->z)));
- }
- return pVal->z;
-}
-
-/* This function is only available internally, it is not part of the
- * external API. It works in a similar way to sql_value_text(),
- * except the data returned is in the encoding specified by the second
- * parameter, which must be one of SQL_UTF16BE, SQL_UTF16LE or
- * SQL_UTF8.
- *
- * (2006-02-16:)  The enc value can be or-ed with SQL_UTF16_ALIGNED.
- * If that is the case, then the result must be aligned on an even byte
- * boundary.
- */
-const void *
-sqlValueText(sql_value * pVal)
-{
- if (!pVal)
-   return 0;
- if ((pVal->flags & (MEM_Str | MEM_Term)) == (MEM_Str | MEM_Term)) {
-   return pVal->z;
- }
- if (pVal->flags & MEM_Null) {
-   return 0;
- }
- return valueToText(pVal);
-}
-
-const char *
-sql_value_to_diag_str(sql_value *value)
-{
- enum mp_type mp_type = sql_value_type(value);
- if (mp_type_is_bloblike(mp_type)) {
-   if (mem_has_msgpack_subtype(value))
-     return sqlValueText(value);
-   return "varbinary";
- }
- return sqlValueText(value);
-}
-
-/*
- * Create a new sql_value object.
- */
-sql_value *
-sqlValueNew(sql * db)
-{
- Mem *p = sqlDbMallocZero(db, sizeof(*p));
- if (p) {
-   p->flags = MEM_Null;
-   p->db = db;
- }
- return p;
-}
-
 /*
  * Context object passed by sqlStat4ProbeSetValue() through to
  * valueNew(). See comments above valueNew() for details.
@@ -1747,125 +653,3 @@ sqlStat4ProbeFree(UnpackedRecord * pRec)
    sqlDbFree(aMem[0].db, pRec);
  }
 }
-
-/*
- * Change the string value of an sql_value object
- */
-void
-sqlValueSetStr(sql_value * v,  /* Value to be set */
-      int n, /* Length of string z */
-      const void *z, /* Text of the new string */
-      void (*xDel) (void *)  /* Destructor for the string */
-    )
-{
- if (v)
-   sqlVdbeMemSetStr((Mem *) v, z, n, 1, xDel);
-}
-
-/*
- * Free an sql_value object
- */
-void
-sqlValueFree(sql_value * v)
-{
- if (!v)
-   return;
- sqlVdbeMemRelease((Mem *) v);
- sqlDbFree(((Mem *) v)->db, v);
-}
-
-/*
- * The sqlValueBytes() routine returns the number of bytes in the
- * sql_value object assuming that it uses the encoding "enc".
- * The valueBytes() routine is a helper function.
- */
-static SQL_NOINLINE int
-valueBytes(sql_value * pVal)
-{
- return valueToText(pVal) != 0 ? pVal->n : 0;
-}
-
-int
-sqlValueBytes(sql_value * pVal)
-{
- Mem *p = (Mem *) pVal;
- assert((p->flags & MEM_Null) == 0
-        || (p->flags & (MEM_Str | MEM_Blob)) == 0);
- if ((p->flags & MEM_Str) != 0) {
-   return p->n;
- }
- if ((p->flags & MEM_Blob) != 0) {
-   if (p->flags & MEM_Zero) {
-     return p->n + p->u.nZero;
-   } else {
-     return p->n;
-   }
- }
- if (p->flags & MEM_Null)
-   return 0;
- return valueBytes(pVal);
-}
-
-void
-mpstream_encode_vdbe_mem(struct mpstream *stream, struct Mem *var)
-{
- assert(memIsValid(var));
- int64_t i;
- if (var->flags & MEM_Null) {
-   mpstream_encode_nil(stream);
- } else if (var->flags & MEM_Real) {
-   mpstream_encode_double(stream, var->u.r);
- } else if (var->flags & MEM_Int) {
-   i = var->u.i;
-   mpstream_encode_int(stream, i);
- } else if (var->flags & MEM_UInt) {
-   i = var->u.u;
-   mpstream_encode_uint(stream, i);
- } else if (var->flags & MEM_Str) {
-   mpstream_encode_strn(stream, var->z, var->n);
- } else if (var->flags & MEM_Bool) {
-   mpstream_encode_bool(stream, var->u.b);
- } else {
-   /*
-    * Emit BIN header iff the BLOB doesn't store
-    * MsgPack content.
-    */
-   if (!mem_has_msgpack_subtype(var)) {
-     uint32_t binl = var->n +
-         ((var->flags & MEM_Zero) ?
-         var->u.nZero : 0);
-     mpstream_encode_binl(stream, binl);
-   }
-   mpstream_memcpy(stream, var->z, var->n);
-   if (var->flags & MEM_Zero)
-     mpstream_memset(stream, 0, var->u.nZero);
- }
-}
-
-char *
-sql_vdbe_mem_encode_tuple(struct Mem *fields, uint32_t field_count,
-       uint32_t *tuple_size, struct region *region)
-{
- size_t used = region_used(region);
- bool is_error = false;
- struct mpstream stream;
- mpstream_init(&stream, region, region_reserve_cb, region_alloc_cb,
-         set_encode_error, &is_error);
- mpstream_encode_array(&stream, field_count);
- for (struct Mem *field = fields; field < fields + field_count; field++)
-   mpstream_encode_vdbe_mem(&stream, field);
- mpstream_flush(&stream);
- if (is_error) {
-   diag_set(OutOfMemory, stream.pos - stream.buf,
-      "mpstream_flush", "stream");
-   return NULL;
- }
- *tuple_size = region_used(region) - used;
- char *tuple = region_join(region, *tuple_size);
- if (tuple == NULL) {
-   diag_set(OutOfMemory, *tuple_size, "region_join", "tuple");
-   return NULL;
- }
- mp_tuple_assert(tuple, tuple + *tuple_size);
- return tuple;
-}
diff --git a/src/box/sql/vdbesort.c b/src/box/sql/vdbesort.c
index a2d681255..927f85559 100644
--- a/src/box/sql/vdbesort.c
+++ b/src/box/sql/vdbesort.c
@@ -152,6 +152,7 @@
  * the main thread to read from.
  */
 #include "sqlInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 
 /*
diff --git a/src/box/sql/vdbetrace.c b/src/box/sql/vdbetrace.c
index 2ee9f668c..e84bb3192 100644
--- a/src/box/sql/vdbetrace.c
+++ b/src/box/sql/vdbetrace.c
@@ -37,6 +37,7 @@
  * The Vdbe parse-tree explainer is also found here.
  */
 #include "sqlInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 
 /*
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 9c4f8b917..e5f35fbf8 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -40,6 +40,7 @@
 #include "coll/coll.h"
 #include "sqlInt.h"
 #include "tarantoolInt.h"
+#include "mem.h"
 #include "vdbeInt.h"
 #include "whereInt.h"
 #include "box/coll_id_cache.h"
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 3811ef3cf..0c002dbee 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -40,6 +40,7 @@
 #include "box/coll_id_cache.h"
 #include "coll/coll.h"
 #include "sqlInt.h"
+#include "mem.h"
 #include "whereInt.h"
 
 /* Forward declarations */

  parent reply	other threads:[~2021-04-09 16:53 UTC|newest]

Thread overview: 107+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-09 16:51 [Tarantool-patches] [PATCH v5 00/52] Move mem-related " Mergen Imeev via Tarantool-patches
2021-04-09 16:51 ` [Tarantool-patches] [PATCH v5 01/52] sql: enhance vdbe_decode_msgpack_into_mem() Mergen Imeev via Tarantool-patches
2021-04-11 17:42   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 12:01     ` Mergen Imeev via Tarantool-patches
2021-04-13 12:12       ` Mergen Imeev via Tarantool-patches
2021-04-13 23:22       ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 23:34         ` Mergen Imeev via Tarantool-patches
2021-04-09 16:51 ` [Tarantool-patches] [PATCH v5 02/52] sql: disable unused code in sql/analyze.c Mergen Imeev via Tarantool-patches
2021-04-09 16:51 ` [Tarantool-patches] [PATCH v5 03/52] sql: disable unused code in sql/legacy.c Mergen Imeev via Tarantool-patches
2021-04-09 16:51 ` [Tarantool-patches] [PATCH v5 04/52] sql: remove NULL-termination in OP_ResultRow Mergen Imeev via Tarantool-patches
2021-04-14 22:23   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-14 22:37     ` Mergen Imeev via Tarantool-patches
2021-04-09 16:51 ` Mergen Imeev via Tarantool-patches [this message]
2021-04-09 16:59 ` [Tarantool-patches] [PATCH v5 06/52] sql: refactor port_vdbemem_*() functions Mergen Imeev via Tarantool-patches
2021-04-09 16:59 ` [Tarantool-patches] [PATCH v5 07/52] sql: remove unused MEM-related functions Mergen Imeev via Tarantool-patches
2021-04-09 16:59 ` [Tarantool-patches] [PATCH v5 08/52] sql: disable unused code in sql/vdbemem.c Mergen Imeev via Tarantool-patches
2021-04-09 16:59 ` [Tarantool-patches] [PATCH v5 09/52] sql: introduce mem_str() Mergen Imeev via Tarantool-patches
2021-04-11 17:44   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 12:36     ` Mergen Imeev via Tarantool-patches
2021-04-14 22:23       ` Vladislav Shpilevoy via Tarantool-patches
2021-04-14 22:42         ` Mergen Imeev via Tarantool-patches
2021-04-09 16:59 ` [Tarantool-patches] [PATCH v5 10/52] sql: introduce mem_create() Mergen Imeev via Tarantool-patches
2021-04-09 17:36 ` [Tarantool-patches] [PATCH v5 11/52] sql: introduce mem_destroy() Mergen Imeev via Tarantool-patches
2021-04-11 17:46   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 12:42     ` Mergen Imeev via Tarantool-patches
2021-04-09 17:36 ` [Tarantool-patches] [PATCH v5 12/52] sql: introduce mem_is_*() functions() Mergen Imeev via Tarantool-patches
2021-04-11 17:59   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 16:09     ` Mergen Imeev via Tarantool-patches
2021-04-14 22:48       ` Vladislav Shpilevoy via Tarantool-patches
2021-04-14 23:07         ` Mergen Imeev via Tarantool-patches
2021-04-09 17:36 ` [Tarantool-patches] [PATCH v5 13/52] sql: introduce mem_copy() Mergen Imeev via Tarantool-patches
2021-04-11 18:06   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 16:18     ` Mergen Imeev via Tarantool-patches
2021-04-09 17:36 ` [Tarantool-patches] [PATCH v5 14/52] sql: introduce mem_copy_as_ephemeral() Mergen Imeev via Tarantool-patches
2021-04-11 18:10   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 16:31     ` Mergen Imeev via Tarantool-patches
2021-04-09 17:37 ` [Tarantool-patches] [PATCH v5 15/52] sql: rework mem_move() Mergen Imeev via Tarantool-patches
2021-04-11 18:10   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 16:38     ` Mergen Imeev via Tarantool-patches
2021-04-09 17:57 ` [Tarantool-patches] [PATCH v5 16/52] sql: rework vdbe_decode_msgpack_into_mem() Mergen Imeev via Tarantool-patches
2021-04-09 17:57 ` [Tarantool-patches] [PATCH v5 17/52] sql: remove sql_column_to_messagepack() Mergen Imeev via Tarantool-patches
2021-04-14 22:58   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-14 23:14     ` Mergen Imeev via Tarantool-patches
2021-04-09 17:57 ` [Tarantool-patches] [PATCH v5 18/52] sql: introduce mem_concat() Mergen Imeev via Tarantool-patches
2021-04-11 18:11   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 16:57     ` Mergen Imeev via Tarantool-patches
2021-04-14 23:04       ` Vladislav Shpilevoy via Tarantool-patches
2021-04-14 23:22         ` Mergen Imeev via Tarantool-patches
2021-04-09 17:57 ` [Tarantool-patches] [PATCH v5 19/52] sql: introduce arithmetic operations for MEM Mergen Imeev via Tarantool-patches
2021-04-11 18:13   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 17:06     ` Mergen Imeev via Tarantool-patches
2021-04-14 23:10       ` Vladislav Shpilevoy via Tarantool-patches
2021-04-14 23:33         ` Mergen Imeev via Tarantool-patches
2021-04-09 17:57 ` [Tarantool-patches] [PATCH v5 20/52] sql: introduce mem_compare() Mergen Imeev via Tarantool-patches
2021-04-11 18:16   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 18:33     ` Mergen Imeev via Tarantool-patches
2021-04-14 23:20       ` Vladislav Shpilevoy via Tarantool-patches
2021-04-14 23:40         ` Mergen Imeev via Tarantool-patches
2021-04-09 18:11 ` [Tarantool-patches] [PATCH v5 21/52] sql: introduce bitwise operations for MEM Mergen Imeev via Tarantool-patches
2021-04-12 23:31   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 20:49     ` Mergen Imeev via Tarantool-patches
2021-04-09 18:11 ` [Tarantool-patches] [PATCH v5 22/52] sql: Initialize MEM in sqlVdbeAllocUnpackedRecord() Mergen Imeev via Tarantool-patches
2021-04-09 18:11 ` [Tarantool-patches] [PATCH v5 23/52] sql: introduce mem_set_null() Mergen Imeev via Tarantool-patches
2021-04-09 18:11 ` [Tarantool-patches] [PATCH v5 24/52] sql: introduce mem_set_int() Mergen Imeev via Tarantool-patches
2021-04-12 23:32   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 20:56     ` Mergen Imeev via Tarantool-patches
2021-04-09 18:11 ` [Tarantool-patches] [PATCH v5 25/52] sql: introduce mem_set_uint() Mergen Imeev via Tarantool-patches
2021-04-09 19:45 ` [Tarantool-patches] [PATCH v5 26/52] sql: move mem_set_bool() and mem_set_double() Mergen Imeev via Tarantool-patches
2021-04-09 19:45 ` [Tarantool-patches] [PATCH v5 27/52] sql: introduce mem_set_str_*() functions Mergen Imeev via Tarantool-patches
2021-04-12 23:34   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 21:36     ` Mergen Imeev via Tarantool-patches
2021-04-14 23:49       ` Vladislav Shpilevoy via Tarantool-patches
2021-04-15  1:25         ` Mergen Imeev via Tarantool-patches
2021-04-09 19:45 ` [Tarantool-patches] [PATCH v5 28/52] sql: introduce mem_copy_str() and mem_copy_str0() Mergen Imeev via Tarantool-patches
2021-04-12 23:35   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 22:00     ` Mergen Imeev via Tarantool-patches
2021-04-14 23:54       ` Vladislav Shpilevoy via Tarantool-patches
2021-04-15  0:30         ` Mergen Imeev via Tarantool-patches
2021-04-09 19:45 ` [Tarantool-patches] [PATCH v5 29/52] sql: introduce mem_set_bin_*() functions Mergen Imeev via Tarantool-patches
2021-04-09 19:45 ` [Tarantool-patches] [PATCH v5 30/52] sql: introduce mem_copy_bin() Mergen Imeev via Tarantool-patches
2021-04-12 23:36   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 22:06     ` Mergen Imeev via Tarantool-patches
2021-04-09 20:05 ` [Tarantool-patches] [PATCH v5 31/52] sql: introduce mem_set_zerobin() Mergen Imeev via Tarantool-patches
2021-04-09 20:05 ` [Tarantool-patches] [PATCH v5 32/52] sql: introduce mem_set_*() for map and array Mergen Imeev via Tarantool-patches
2021-04-12 23:36   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 22:08     ` Mergen Imeev via Tarantool-patches
2021-04-09 20:05 ` [Tarantool-patches] [PATCH v5 33/52] sql: introduce mem_set_invalid() Mergen Imeev via Tarantool-patches
2021-04-09 20:05 ` [Tarantool-patches] [PATCH v5 34/52] sql: refactor mem_set_ptr() Mergen Imeev via Tarantool-patches
2021-04-09 20:05 ` [Tarantool-patches] [PATCH v5 35/52] sql: introduce mem_set_frame() Mergen Imeev via Tarantool-patches
2021-04-12 23:37   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 22:19     ` Mergen Imeev via Tarantool-patches
2021-04-09 20:25 ` [Tarantool-patches] [PATCH v5 36/52] sql: introduce mem_set_agg() Mergen Imeev via Tarantool-patches
2021-04-12 23:37   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 22:46     ` Mergen Imeev via Tarantool-patches
2021-04-09 20:25 ` [Tarantool-patches] [PATCH v5 37/52] sql: introduce mem_set_null_clear() Mergen Imeev via Tarantool-patches
2021-04-12 23:38   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 22:50     ` Mergen Imeev via Tarantool-patches
2021-04-09 20:25 ` [Tarantool-patches] [PATCH v5 38/52] sql: move MEM flags to mem.c Mergen Imeev via Tarantool-patches
2021-04-13 20:42   ` Mergen Imeev via Tarantool-patches
2021-04-09 20:25 ` [Tarantool-patches] [PATCH v5 39/52] sql: introduce mem_to_int*() functions Mergen Imeev via Tarantool-patches
2021-04-12 23:39   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13 22:58     ` Mergen Imeev via Tarantool-patches
2021-04-13 23:10       ` Mergen Imeev via Tarantool-patches
2021-04-09 20:26 ` [Tarantool-patches] [PATCH v5 40/52] sql: introduce mem_to_double() Mergen Imeev via Tarantool-patches
2021-04-13 23:21   ` Mergen Imeev via Tarantool-patches
2021-04-15  0:39 ` [Tarantool-patches] [PATCH v5 00/52] Move mem-related functions to mem.c/mem.h Vladislav Shpilevoy via Tarantool-patches
2021-04-15  6:49 ` Kirill Yukhin via Tarantool-patches

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=16b3a31eb6de710a3512d38a40ca8fd52bb50bca.1617984948.git.imeevma@gmail.com \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=imeevma@tarantool.org \
    --cc=tsafin@tarantool.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH v5 05/52] sql: move MEM-related functions to mem.c/mem.h' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox