[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