[Tarantool-patches] [PATCH v4 05/53] sql: move MEM-related functions to mem.c/mem.h

imeevma at tarantool.org imeevma at tarantool.org
Tue Mar 23 12:34:58 MSK 2021


This patch moves all MEM-related functions in mem.c/mem.h.

Part of #5818
---
 src/box/CMakeLists.txt  |    1 +
 src/box/sql.c           |    1 +
 src/box/sql/analyze.c   |    1 +
 src/box/sql/build.c     |    1 +
 src/box/sql/func.c      |    1 +
 src/box/sql/insert.c    |    1 +
 src/box/sql/main.c      |    1 +
 src/box/sql/mem.c       | 2376 +++++++++++++++++++++++++++++++++++++++
 src/box/sql/mem.h       |  502 +++++++++
 src/box/sql/pragma.c    |    1 +
 src/box/sql/printf.c    |    1 +
 src/box/sql/select.c    |    1 +
 src/box/sql/sqlInt.h    |   50 -
 src/box/sql/trigger.c   |    1 +
 src/box/sql/vdbe.c      |  471 +-------
 src/box/sql/vdbe.h      |    1 -
 src/box/sql/vdbeInt.h   |  299 -----
 src/box/sql/vdbeapi.c   |  137 +--
 src/box/sql/vdbeaux.c   |  567 +---------
 src/box/sql/vdbemem.c   | 1241 +-------------------
 src/box/sql/vdbesort.c  |    1 +
 src/box/sql/vdbetrace.c |    1 +
 src/box/sql/where.c     |    1 +
 src/box/sql/whereexpr.c |    1 +
 24 files changed, 2897 insertions(+), 2762 deletions(-)
 create mode 100644 src/box/sql/mem.c
 create mode 100644 src/box/sql/mem.h

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 f15d27051..074d41260 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"
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..62338e1db
--- /dev/null
+++ b/src/box/sql/mem.c
@@ -0,0 +1,2376 @@
+/*
+ * 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"
+
+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
+/*
+ * 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)
+{
+	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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Print the SQL that was used to generate a VDBE program.
+ */
+void
+sqlVdbePrintSql(Vdbe * p)
+{
+	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);
+}
+
+/*
+ * 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_MAX;
+		} 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_MAX;
+		} 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;
+}
diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h
new file mode 100644
index 000000000..e78ebbe47
--- /dev/null
+++ b/src/box/sql/mem.h
@@ -0,0 +1,502 @@
+#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
+void sqlVdbeMemAboutToChange(struct Vdbe *, struct Mem *);
+int sqlVdbeCheckMemInvariants(struct Mem *);
+void sqlVdbePrintSql(Vdbe *);
+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
+
+/*
+ * Invoke this macro on memory cells just prior to changing the
+ * value of the cell.  This macro verifies that shallow copies are
+ * not misused.  A shallow copy of a string or blob just copies a
+ * pointer to the string or blob, not the content.  If the original
+ * is changed while the copy is still in use, the string or blob might
+ * be changed out from under the copy.  This macro verifies that nothing
+ * like that ever happens.
+ */
+#ifdef SQL_DEBUG
+# define memAboutToChange(P,M) sqlVdbeMemAboutToChange(P,M)
+#else
+# define memAboutToChange(P,M)
+#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..21abdcbd5 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"
 
@@ -57,21 +58,6 @@
 #include "box/sequence.h"
 #include "box/session_settings.h"
 
-/*
- * Invoke this macro on memory cells just prior to changing the
- * value of the cell.  This macro verifies that shallow copies are
- * not misused.  A shallow copy of a string or blob just copies a
- * pointer to the string or blob, not the content.  If the original
- * is changed while the copy is still in use, the string or blob might
- * be changed out from under the copy.  This macro verifies that nothing
- * like that ever happens.
- */
-#ifdef SQL_DEBUG
-# define memAboutToChange(P,M) sqlVdbeMemAboutToChange(P,M)
-#else
-# define memAboutToChange(P,M)
-#endif
-
 /*
  * The following global variable is incremented every time a cursor
  * moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes.  The test
@@ -257,424 +243,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 +295,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..089ac7575 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 *);
@@ -596,57 +364,12 @@ int sqlVdbeSorterRewind(const VdbeCursor *, int *);
 int sqlVdbeSorterWrite(const VdbeCursor *, Mem *);
 int sqlVdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
 
-#ifdef SQL_DEBUG
-void sqlVdbeMemAboutToChange(Vdbe *, Mem *);
-int sqlVdbeCheckMemInvariants(Mem *);
-#endif
-
 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 +381,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 90c4b73c0..9153df1df 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().
@@ -1447,30 +1389,6 @@ sqlVdbeList(Vdbe * p)
 	return rc;
 }
 
-#ifdef SQL_DEBUG
-/*
- * Print the SQL that was used to generate a VDBE program.
- */
-void
-sqlVdbePrintSql(Vdbe * p)
-{
-	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);
-}
-#endif
-
-
 /* An instance of this object describes bulk memory available for use
  * by subcomponents of a prepared statement.  Space is allocated out
  * of a ReusableSpace object by the allocSpace() routine below.
@@ -2334,204 +2252,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 +2325,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_MAX;
-		} 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_MAX;
-		} 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..d1a9f7e06 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -37,1057 +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
- * 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)
-{
-	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;
-}
-#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 +109,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 +630,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 */
-- 
2.25.1



More information about the Tarantool-patches mailing list