From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id B5BD06EC6F; Sat, 13 Feb 2021 18:39:30 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org B5BD06EC6F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1613230770; bh=y9bpt6iMl1JApFwLhpwpEiwlNzmuLDtLCfigY2Qkwss=; h=To:Cc:Date:In-Reply-To:References:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=iskg2gR35iyo3HXzFPHcTwD5M4Ll9D35Vx9ZsxCDCIW6mKngC7mqEIv7X1fiXni87 DIp2edcmNzX1kClnYLlrjPwoo6GvrJAAZdnu0VBePPGJeRBvvnKzKgFT7/6sTDGXsp WwOBIhNHc6ACpS8HFy+iIWUkntuxaGVHZrJipNOc= Received: from smtpng1.m.smailru.net (smtpng1.m.smailru.net [94.100.181.251]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 4FA676EC6F for ; Sat, 13 Feb 2021 18:38:36 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 4FA676EC6F Received: by smtpng1.m.smailru.net with esmtpa (envelope-from ) id 1lAx0B-0006B0-S1; Sat, 13 Feb 2021 18:38:32 +0300 To: s.ostanevich@corp.mail.ru, tsafin@tarantool.org Cc: tarantool-patches@dev.tarantool.org Date: Sat, 13 Feb 2021 18:38:31 +0300 Message-Id: <338630dcd1ca47e8998d257bcc5886fae823e808.1613230498.git.imeevma@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-618D5548: 7C25F47CF0CF76AC044D002BEF27BE963E66A9355E9453899EA5F276FC8F6C60 X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD981647AC6901E234BAEBD76BEDCA450D6CA8258D9A07F13E2182A05F5380850408F047E29E6B739AA5F6003AFAC4FFAC63C21CEA5A0471BB4DAFBDC0746A179C1 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE787D390A33DAA1DB5EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637E5A8E5ECB3FF24018638F802B75D45FF5571747095F342E8C7A0BC55FA0FE5FC2CDB662838BF7A90DDF7EC5E02AF6C4C0B8E9889A5DD31CE389733CBF5DBD5E913377AFFFEAFD269176DF2183F8FC7C07E7E81EEA8A9722B8941B15DA834481FCF19DD082D7633A0EF3E4896CB9E6436389733CBF5DBD5E9D5E8D9A59859A8B652D31B9D28593E51CC7F00164DA146DA6F5DAA56C3B73B23C77107234E2CFBA567F23339F89546C55F5C1EE8F4F765FCB835E6E385EA5AF075ECD9A6C639B01BBD4B6F7A4D31EC0BC0CAF46E325F83A522CA9DD8327EE4930A3850AC1BE2E7355E1C53F199C2BB95B5C8C57E37DE458B4C7702A67D5C3316FA3894348FB808DB853643B3A886106C3B503F486389A921A5CC5B56E945C8DA X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CD27AC6BE5C1CDEA937B059441E244C083A6A93724AACD0099C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EF31C0090ACECF247D699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34D4E96E2A5B1100E01305DB33A78A6CD0A59CE02E3BA2C795A5C26299BB5E5AC52CEE9424DE9BBE7E1D7E09C32AA3244CE4DDCB8AFE62343B0DF0B1420E2D4B3233C9DC155518937FFACE5A9C96DEB163 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2bioj+JvDbeHF34znq+1FVBKJXA== X-Mailru-Sender: 689FA8AB762F73936BC43F508A06382264CE11E51B67B312A877B0C78495495883D72C36FC87018B9F80AB2734326CD2FB559BB5D741EB96352A0ABBE4FDA4210A04DAD6CC59E33667EA787935ED9F1B X-Mras: Ok Subject: [Tarantool-patches] [PATCH v2 2/2] sql: encapsulate setting type checking of MEM X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Mergen Imeev via Tarantool-patches Reply-To: imeevma@tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" This patch encapsulates checking the MEM type and setting a new MEM value. Part of #4470 --- src/box/sql/func.c | 2 +- src/box/sql/vdbe.c | 529 ++++++++++++++++++---------------------- src/box/sql/vdbeInt.h | 204 ++++++++++++++++ src/box/sql/vdbeapi.c | 53 ++-- src/box/sql/vdbeaux.c | 273 ++++++++++----------- src/box/sql/vdbemem.c | 116 +++++++++ src/box/sql/vdbesort.c | 9 +- src/box/sql/vdbetrace.c | 12 +- 8 files changed, 714 insertions(+), 484 deletions(-) diff --git a/src/box/sql/func.c b/src/box/sql/func.c index f15d27051..3fd1cabec 100644 --- a/src/box/sql/func.c +++ b/src/box/sql/func.c @@ -94,7 +94,7 @@ vdbemem_alloc_on_region(uint32_t count) } memset(ret, 0, count * sizeof(*ret)); for (uint32_t i = 0; i < count; i++) { - sqlVdbeMemInit(&ret[i], sql_get(), MEM_Null); + mem_init(&ret[i]); assert(memIsValid(&ret[i])); } return ret; diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 3b3b1f01d..5089fff18 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -116,7 +116,7 @@ int sql_max_blobsize = 0; static void updateMaxBlobsize(Mem *p) { - if ((p->flags & (MEM_Str|MEM_Blob))!=0 && p->n>sql_max_blobsize) { + if (mem_is_varstring(p) && p->n>sql_max_blobsize) { sql_max_blobsize = p->n; } } @@ -185,7 +185,7 @@ vdbeTakeBranch(int iSrcLine, u8 I, u8 M) * already. Return non-zero if a malloc() fails. */ #define Stringify(P) \ - if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlVdbeMemStringify(P)) \ + if(!mem_is_varstring(P) && sqlVdbeMemStringify(P)) \ { goto no_mem; } /* @@ -260,7 +260,7 @@ allocateCursor( int mem_apply_numeric_type(struct Mem *record) { - if ((record->flags & MEM_Str) == 0) + if (!mem_is_string(record)) return -1; int64_t integer_value; bool is_neg; @@ -311,17 +311,17 @@ mem_apply_numeric_type(struct Mem *record) static int mem_apply_type(struct Mem *record, enum field_type type) { - if ((record->flags & MEM_Null) != 0) + if (mem_is_null(record)) 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) + if (mem_is_bool(record) || mem_is_binary(record)) return -1; - if ((record->flags & MEM_UInt) == MEM_UInt) + if (mem_is_pos_int(record)) return 0; - if ((record->flags & MEM_Real) == MEM_Real) { + if (mem_is_double(record)) { double d = record->u.r; if (d >= 0) { if (double_compare_uint64(d, UINT64_MAX, @@ -337,29 +337,29 @@ mem_apply_type(struct Mem *record, enum field_type type) } return 0; } - if ((record->flags & MEM_Str) != 0) { + if (mem_is_string(record)) { 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 (mem_is_neg_int(record)) { if (type == FIELD_TYPE_UNSIGNED) return -1; return 0; } return 0; case FIELD_TYPE_BOOLEAN: - if ((record->flags & MEM_Bool) == MEM_Bool) + if (mem_is_bool(record)) return 0; return -1; case FIELD_TYPE_NUMBER: - if ((record->flags & (MEM_Real | MEM_Int | MEM_UInt)) != 0) + if (mem_is_number(record)) return 0; return sqlVdbeMemRealify(record); case FIELD_TYPE_DOUBLE: - if ((record->flags & MEM_Real) != 0) + if (mem_is_double(record)) return 0; return sqlVdbeMemRealify(record); case FIELD_TYPE_STRING: @@ -368,34 +368,24 @@ mem_apply_type(struct Mem *record, enum field_type type) * 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) + if (!mem_is_string(record) && mem_is_number(record)) sqlVdbeMemStringify(record); - record->flags &= ~(MEM_Real | MEM_Int | MEM_UInt); return 0; case FIELD_TYPE_VARBINARY: - if ((record->flags & MEM_Blob) == 0) + if (!mem_is_binary(record)) 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); + if (mem_is_map(record) || mem_is_array(record)) 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) + if (mem_is_map(record)) 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) + if (mem_is_array(record)) return 0; return -1; case FIELD_TYPE_ANY: @@ -442,12 +432,12 @@ mem_is_type_compatible(struct Mem *mem, enum field_type type) static int mem_convert_to_double(struct Mem *mem) { - if ((mem->flags & MEM_Real) != 0) + if (mem_is_double(mem)) return 0; - if ((mem->flags & (MEM_Int | MEM_UInt)) == 0) + if (!mem_is_integer(mem)) return -1; double d; - if ((mem->flags & MEM_Int) != 0) + if (mem_is_neg_int(mem)) d = (double)mem->u.i; else d = (double)mem->u.u; @@ -464,11 +454,11 @@ mem_convert_to_double(struct Mem *mem) static int mem_convert_to_unsigned(struct Mem *mem) { - if ((mem->flags & MEM_UInt) != 0) + if (mem_is_pos_int(mem)) return 0; - if ((mem->flags & MEM_Int) != 0) + if (mem_is_neg_int(mem)) return -1; - if ((mem->flags & MEM_Real) == 0) + if (!mem_is_double(mem)) return -1; double d = mem->u.r; if (d < 0.0 || d >= (double)UINT64_MAX) @@ -486,9 +476,9 @@ mem_convert_to_unsigned(struct Mem *mem) static int mem_convert_to_integer(struct Mem *mem) { - if ((mem->flags & (MEM_UInt | MEM_Int)) != 0) + if (mem_is_integer(mem)) return 0; - if ((mem->flags & MEM_Real) == 0) + if (!mem_is_double(mem)) return -1; double d = mem->u.r; if (d >= (double)UINT64_MAX || d < (double)INT64_MIN) @@ -525,36 +515,33 @@ mem_convert_to_numeric(struct Mem *mem, enum field_type type) /* * 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. + * numeric type, if has one. Set the tmp_mem accordingly. */ -static u16 SQL_NOINLINE computeNumericType(Mem *pMem) +static struct Mem * +SQL_NOINLINE computeNumericType(struct Mem *pMem, struct Mem *tmp_mem) { - 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; + assert(!mem_is_number(pMem)); + assert(mem_is_varstring(pMem)); + double r; + if (sqlAtoF(pMem->z, &r, pMem->n) == 0) + return pMem; 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; + int64_t i; + if (sql_atoi64(pMem->z, &i, &is_neg, pMem->n) == 0) + mem_set_int(tmp_mem, i, is_neg); + else + mem_set_double(tmp_mem, r); + return tmp_mem; } -/* - * 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) +/** Return adress of the mem that contains numeric value for pMem. */ +static struct Mem * +numericType(struct Mem *pMem, struct Mem *tmp_mem) { - 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); + if (mem_is_varstring(pMem)) { + return computeNumericType(pMem, tmp_mem); } - return 0; + return pMem; } #ifdef SQL_DEBUG @@ -568,7 +555,7 @@ sqlVdbeMemPrettyPrint(Mem *pMem, char *zBuf) char *zCsr = zBuf; int f = pMem->flags; - if (f&MEM_Blob) { + if (mem_is_binary(pMem)) { int i; char c; if (f & MEM_Dyn) { @@ -604,7 +591,7 @@ sqlVdbeMemPrettyPrint(Mem *pMem, char *zBuf) zCsr += sqlStrlen30(zCsr); } *zCsr = '\0'; - } else if (f & MEM_Str) { + } else if (mem_is_string(pMem)) { int j, k; zBuf[0] = ' '; if (f & MEM_Dyn) { @@ -646,19 +633,17 @@ sqlVdbeMemPrettyPrint(Mem *pMem, char *zBuf) static void memTracePrint(Mem *p) { - if (p->flags & MEM_Undefined) { + if (mem_is_undefined(p)) { printf(" undefined"); - } else if (p->flags & MEM_Null) { + } else if (mem_is_null(p)) { 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) { + } else if (mem_is_neg_int(p)) { printf(" i:%lld", p->u.i); - } else if (p->flags & MEM_UInt) { + } else if (mem_is_pos_int(p)) { printf(" u:%"PRIu64"", p->u.u); - } else if (p->flags & MEM_Real) { + } else if (mem_is_double(p)) { printf(" r:%g", p->u.r); - } else if (p->flags & MEM_Bool) { + } else if (mem_is_bool(p)) { printf(" bool:%s", SQL_TOKEN_BOOLEAN(p->u.b)); } else { char zBuf[200]; @@ -700,8 +685,7 @@ vdbe_prepare_null_out(struct Vdbe *v, int n) assert(n <= (v->nMem + 1 - v->nCursor)); struct Mem *out = &v->aMem[n]; memAboutToChange(v, out); - sqlVdbeMemSetNull(out); - out->field_type = field_type_MAX; + mem_set_null(out); return out; } @@ -731,35 +715,32 @@ char * mem_type_to_str(const struct Mem *p) { assert(p != NULL); - switch (p->flags & MEM_PURE_TYPE_MASK) { - case MEM_Null: + if (mem_is_null(p)) return "NULL"; - case MEM_Str: + if (mem_is_string(p)) return "text"; - case MEM_Int: + if (mem_is_neg_int(p)) return "integer"; - case MEM_UInt: + if (mem_is_pos_int(p)) return "unsigned"; - case MEM_Real: + if (mem_is_double(p)) return "real"; - case MEM_Blob: + if (mem_is_binary(p)) return "varbinary"; - case MEM_Bool: - return "boolean"; - default: - unreachable(); - } + assert(mem_is_bool(p)); + return "boolean"; } /* 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) + char *buf = region_alloc(&fiber()->gc, size); + if (buf == NULL) { + diag_set(OutOfMemory, size, "region_alloc", "buf"); return -1; - vdbe_mem->flags = MEM_Ephem | MEM_Blob; + } + mem_set_bin(vdbe_mem, buf, size, MEM_Ephem, false); assert(sqlVdbeCheckMemInvariants(vdbe_mem)); return 0; } @@ -884,29 +865,20 @@ vdbe_field_ref_fetch(struct vdbe_field_ref *field_ref, uint32_t fieldno, * Wrap it in a blob verbatim. */ if (dest_mem->flags == 0) { - dest_mem->z = (char *) data; - dest_mem->n = vdbe_field_ref_fetch_data(field_ref, - fieldno + 1) - data; - dest_mem->flags = MEM_Blob | MEM_Ephem | MEM_Subtype; - dest_mem->subtype = SQL_SUBTYPE_MSGPACK; + uint32_t size = vdbe_field_ref_fetch_data(field_ref, + fieldno + 1) - data; + if (mp_typeof(*data) == MP_MAP) + mem_set_map(dest_mem, (char *)data, size, MEM_Ephem); + else + mem_set_array(dest_mem, (char *)data, size, MEM_Ephem); } - /* - * Add 0 termination (at most for strings) - * Not sure why do we check MEM_Ephem - */ - if ((dest_mem->flags & (MEM_Ephem | MEM_Str)) == - (MEM_Ephem | MEM_Str)) { - int len = dest_mem->n; - if (dest_mem->szMalloc < len + 1) { - if (sqlVdbeMemGrow(dest_mem, len + 1, 1) != 0) - return -1; + if (mem_is_string(dest_mem) && (dest_mem->flags & MEM_Ephem) != 0) { + if (dest_mem->n > 0) { + mem_set_str(dest_mem, dest_mem->z, dest_mem->n, 0, + (dest_mem->flags & MEM_Term) != 0); } else { - dest_mem->z = - memcpy(dest_mem->zMalloc, dest_mem->z, len); - dest_mem->flags &= ~MEM_Ephem; + mem_set_str(dest_mem, "", 0, MEM_Static, true); } - dest_mem->z[len] = 0; - dest_mem->flags |= MEM_Term; } UPDATE_MAX_BLOBSIZE(dest_mem); dest_mem->field_type = vdbe_field_ref_fetch_type(field_ref, fieldno); @@ -1129,9 +1101,9 @@ case OP_Gosub: { /* jump */ */ case OP_Return: { /* in1 */ pIn1 = &aMem[pOp->p1]; - assert(pIn1->flags==MEM_UInt); + assert(mem_is_pos_int(pIn1)); pOp = &aOp[pIn1->u.u]; - pIn1->flags = MEM_Undefined; + mem_set_undefined(pIn1); break; } @@ -1168,13 +1140,13 @@ case OP_InitCoroutine: { /* jump */ case OP_EndCoroutine: { /* in1 */ VdbeOp *pCaller; pIn1 = &aMem[pOp->p1]; - assert(pIn1->flags == MEM_UInt); + assert(mem_is_pos_int(pIn1)); assert(pIn1->u.u < (uint64_t) p->nOp); pCaller = &aOp[pIn1->u.u]; assert(pCaller->opcode==OP_Yield); assert(pCaller->p2>=0 && pCaller->p2nOp); pOp = &aOp[pCaller->p2 - 1]; - pIn1->flags = MEM_Undefined; + mem_set_undefined(pIn1); break; } @@ -1342,10 +1314,8 @@ case OP_String8: { /* same as TK_STRING, out2 */ */ case OP_String: { /* out2 */ assert(pOp->p4.z!=0); - pOut = vdbe_prepare_null_out(p, pOp->p2); - pOut->flags = MEM_Str|MEM_Static|MEM_Term; - pOut->z = pOp->p4.z; - pOut->n = pOp->p1; + pOut = &p->aMem[pOp->p2]; + mem_set_str(pOut, pOp->p4.z, pOp->p1, MEM_Static, true); UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -1364,19 +1334,17 @@ case OP_String: { /* out2 */ */ case OP_Null: { /* out2 */ int cnt; - u16 nullFlag; pOut = vdbe_prepare_null_out(p, pOp->p2); cnt = pOp->p3-pOp->p2; assert(pOp->p3<=(p->nMem+1 - p->nCursor)); - pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null; - pOut->n = 0; + if (pOp->p1) + pOut->flags |= MEM_Cleared; while( cnt>0) { pOut++; memAboutToChange(p, pOut); - sqlVdbeMemSetNull(pOut); - pOut->flags = nullFlag; - pOut->field_type = field_type_MAX; - pOut->n = 0; + mem_set_null(pOut); + if (pOp->p1) + pOut->flags |= MEM_Cleared; cnt--; } break; @@ -1390,12 +1358,13 @@ case OP_Null: { /* out2 */ */ case OP_Blob: { /* out2 */ assert(pOp->p1 <= SQL_MAX_LENGTH); - pOut = vdbe_prepare_null_out(p, pOp->p2); - sqlVdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); - if (pOp->p3!=0) { - pOut->flags |= MEM_Subtype; - pOut->subtype = pOp->p3; - } + pOut = &p->aMem[pOp->p2]; + if (pOp->p3 == 0) + mem_set_bin(pOut, pOp->p4.z, pOp->p1, MEM_Static, false); + else if (mp_typeof(*pOp->p4.z) == MP_MAP) + mem_set_map(pOut, pOp->p4.z, pOp->p1, MEM_Static); + else + mem_set_array(pOut, pOp->p4.z, pOp->p1, MEM_Static); UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -1547,7 +1516,7 @@ case OP_ResultRow: { assert(memIsValid(&pMem[i])); Deephemeralize(&pMem[i]); assert((pMem[i].flags & MEM_Ephem)==0 - || (pMem[i].flags & (MEM_Str|MEM_Blob))==0); + || !mem_is_varstring(&pMem[i])); sqlVdbeMemNulTerminate(&pMem[i]); REGISTER_TRACE(p, pOp->p1+i, &pMem[i]); } @@ -1587,7 +1556,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ pIn2 = &aMem[pOp->p2]; pOut = vdbe_prepare_null_out(p, pOp->p3); assert(pIn1!=pOut); - if ((pIn1->flags | pIn2->flags) & MEM_Null) { + if (mem_is_null(pIn1) || mem_is_null(pIn2)) { /* Force NULL be of type STRING. */ pOut->field_type = FIELD_TYPE_STRING; break; @@ -1596,10 +1565,8 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ * Concatenation operation can be applied only to * strings and blobs. */ - uint32_t str_type_p1 = pIn1->flags & (MEM_Blob | MEM_Str); - uint32_t str_type_p2 = pIn2->flags & (MEM_Blob | MEM_Str); - if (str_type_p1 == 0 || str_type_p2 == 0) { - char *inconsistent_type = str_type_p1 == 0 ? + if (!mem_is_varstring(pIn1) || !mem_is_varstring(pIn2)) { + char *inconsistent_type = !mem_is_varstring(pIn1) ? mem_type_to_str(pIn1) : mem_type_to_str(pIn2); diag_set(ClientError, ER_INCONSISTENT_TYPES, @@ -1608,7 +1575,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ } /* Moreover, both operands must be of the same type. */ - if (str_type_p1 != str_type_p2) { + if (!mems_have_same_type(pIn1, pIn2)) { diag_set(ClientError, ER_INCONSISTENT_TYPES, mem_type_to_str(pIn2), mem_type_to_str(pIn1)); goto abort_due_to_error; @@ -1619,21 +1586,19 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ if (nByte>db->aLimit[SQL_LIMIT_LENGTH]) { goto too_big; } - if (sqlVdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2)) { - goto no_mem; + size_t svp = region_used(&fiber()->gc); + char *buf = region_alloc(&fiber()->gc, nByte); + if (buf == NULL) { + diag_set(OutOfMemory, nByte, "region_alloc", "buf"); + goto abort_due_to_error; } - if (pIn1->flags & MEM_Str) - MemSetTypeFlag(pOut, MEM_Str); + memcpy(buf, pIn2->z, pIn2->n); + memcpy(&buf[pIn2->n], pIn1->z, pIn1->n); + if (mem_is_binary(pIn1)) + mem_set_bin(pOut, buf, nByte, 0, false); else - MemSetTypeFlag(pOut, MEM_Blob); - if (pOut!=pIn2) { - memcpy(pOut->z, pIn2->z, pIn2->n); - } - memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n); - pOut->z[nByte]=0; - pOut->z[nByte+1] = 0; - pOut->flags |= MEM_Term; - pOut->n = (int)nByte; + mem_set_str(pOut, buf, nByte, 0, false); + region_truncate(&fiber()->gc, svp); UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -1681,27 +1646,26 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ - u32 flags; /* Combined MEM_* flags from both inputs */ - u16 type1; /* Numeric type of left operand */ - u16 type2; /* Numeric type of right operand */ i64 iA; /* Integer value of left operand */ i64 iB; /* Integer value of right operand */ double rA; /* Real value of left operand */ double rB; /* Real value of right operand */ + struct Mem tmp_mem1, tmp_mem2, *mem1, *mem2; + mem_init(&tmp_mem1); + mem_init(&tmp_mem2); pIn1 = &aMem[pOp->p1]; - type1 = numericType(pIn1); + mem1 = numericType(&aMem[pOp->p1], &tmp_mem1); pIn2 = &aMem[pOp->p2]; - type2 = numericType(pIn2); + mem2 = numericType(&aMem[pOp->p2], &tmp_mem2); pOut = vdbe_prepare_null_out(p, pOp->p3); - flags = pIn1->flags | pIn2->flags; - if ((flags & MEM_Null)!=0) goto arithmetic_result_is_null; - if ((type1 & (MEM_Int | MEM_UInt)) != 0 && - (type2 & (MEM_Int | MEM_UInt)) != 0) { - iA = pIn1->u.i; - iB = pIn2->u.i; - bool is_lhs_neg = pIn1->flags & MEM_Int; - bool is_rhs_neg = pIn2->flags & MEM_Int; + if (mem_is_null(mem1) || mem_is_null(mem2)) + goto arithmetic_result_is_null; + if (mem_is_integer(mem1) && mem_is_integer(mem2)) { + iA = mem1->u.i; + iB = mem2->u.i; + bool is_lhs_neg = mem_is_neg_int(pIn1); + bool is_rhs_neg = mem_is_neg_int(pIn2); bool is_res_neg; switch( pOp->opcode) { case OP_Add: { @@ -1752,7 +1716,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ sql_value_to_diag_str(pIn2), "numeric"); goto abort_due_to_error; } - assert(((type1 | type2) & MEM_Real) != 0); + assert(mem_is_double(mem1) || mem_is_double(mem2)); switch( pOp->opcode) { case OP_Add: rB += rA; break; case OP_Subtract: rB -= rA; break; @@ -1908,7 +1872,7 @@ case OP_BuiltinFunction: { goto abort_due_to_error; /* Copy the result of the function into register P3 */ - if (pOut->flags & (MEM_Str|MEM_Blob)) { + if (mem_is_varstring(pOut)) { if (sqlVdbeMemTooBig(pCtx->pOut)) goto too_big; } @@ -1967,7 +1931,7 @@ case OP_FunctionByName: { * Copy the result of the function invocation into * register P3. */ - if ((pOut->flags & (MEM_Str | MEM_Blob)) != 0) + if (mem_is_varstring(pOut)) if (sqlVdbeMemTooBig(pOut)) goto too_big; REGISTER_TRACE(p, pOp->p3, pOut); @@ -2017,7 +1981,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; pOut = vdbe_prepare_null_out(p, pOp->p3); - if ((pIn1->flags | pIn2->flags) & MEM_Null) { + if (mem_is_null(pIn1) || mem_is_null(pIn2)) { /* Force NULL be of type INTEGER. */ pOut->field_type = FIELD_TYPE_INTEGER; break; @@ -2076,7 +2040,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); - assert((pIn1->flags & MEM_UInt) != 0 && pOp->p2 >= 0); + assert(mem_is_pos_int(pIn1) && pOp->p2 >= 0); pIn1->u.u += pOp->p2; break; } @@ -2090,9 +2054,9 @@ case OP_AddImm: { /* in1 */ */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - if ((pIn1->flags & (MEM_Int | MEM_UInt)) == 0) { + if (!mem_is_integer(pIn1)) { mem_apply_type(pIn1, FIELD_TYPE_INTEGER); - if ((pIn1->flags & (MEM_Int | MEM_UInt)) == 0) { + if (!mem_is_integer(pIn1)) { if (pOp->p2==0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_to_diag_str(pIn1), "integer"); @@ -2116,7 +2080,7 @@ case OP_MustBeInt: { /* jump, in1 */ */ case OP_Realify: { /* in1 */ pIn1 = &aMem[pOp->p1]; - if ((pIn1->flags & (MEM_Int | MEM_UInt)) != 0) { + if (mem_is_integer(pIn1)) { sqlVdbeMemRealify(pIn1); } break; @@ -2248,16 +2212,10 @@ case OP_Le: /* same as TK_LE, jump, in1, in3 */ case OP_Gt: /* same as TK_GT, jump, in1, in3 */ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ int res, res2; /* Result of the comparison of pIn1 against pIn3 */ - u32 flags1; /* Copy of initial value of pIn1->flags */ - u32 flags3; /* Copy of initial value of pIn3->flags */ pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; - flags1 = pIn1->flags; - flags3 = pIn3->flags; - enum field_type ft_p1 = pIn1->field_type; - enum field_type ft_p3 = pIn3->field_type; - if ((flags1 | flags3)&MEM_Null) { + if (mem_is_null(pIn1) || mem_is_null(pIn3)) { /* One or both operands are NULL */ if (pOp->p5 & SQL_NULLEQ) { /* If SQL_NULLEQ is set (which will only happen if the operator is @@ -2265,10 +2223,10 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ * or not both operands are null. */ assert(pOp->opcode==OP_Eq || pOp->opcode==OP_Ne); - assert((flags1 & MEM_Cleared)==0); + assert((pIn1->flags & MEM_Cleared)==0); assert((pOp->p5 & SQL_JUMPIFNULL)==0); - if ((flags1&flags3&MEM_Null)!=0 - && (flags3&MEM_Cleared)==0 + if (mem_is_null(pIn1) && mem_is_null(pIn3) + && (pIn3->flags & MEM_Cleared)==0 ) { res = 0; /* Operands are equal */ } else { @@ -2291,22 +2249,22 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ } break; } - } else if (((flags1 | flags3) & MEM_Bool) != 0 || - ((flags1 | flags3) & MEM_Blob) != 0) { + } else if (mem_is_bool(pIn1) || mem_is_bool(pIn3) || + mem_is_binary(pIn1) || mem_is_binary(pIn3)) { /* * If one of values is of type BOOLEAN or VARBINARY, * then the second one must be of the same type as * well. Otherwise an error is raised. */ - int type_arg1 = flags1 & (MEM_Bool | MEM_Blob); - int type_arg3 = flags3 & (MEM_Bool | MEM_Blob); - if (type_arg1 != type_arg3) { - char *inconsistent_type = type_arg1 != 0 ? + if (!mems_have_same_type(pIn1, pIn3)) { + char *inconsistent_type = mem_is_bool(pIn1) || + mem_is_binary(pIn1) ? mem_type_to_str(pIn3) : mem_type_to_str(pIn1); - char *expected_type = type_arg1 != 0 ? - mem_type_to_str(pIn1) : - mem_type_to_str(pIn3); + char *expected_type = mem_is_bool(pIn1) || + mem_is_binary(pIn1) ? + mem_type_to_str(pIn1) : + mem_type_to_str(pIn3); diag_set(ClientError, ER_SQL_TYPE_MISMATCH, inconsistent_type, expected_type); goto abort_due_to_error; @@ -2314,18 +2272,25 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ res = sqlMemCompare(pIn3, pIn1, NULL); } else { enum field_type type = pOp->p5 & FIELD_TYPE_MASK; + struct Mem tmp_mem1, tmp_mem2, *mem1, *mem2; + mem1 = pIn1; + mem2 = pIn3; if (sql_type_is_numeric(type)) { - if ((flags1 | flags3)&MEM_Str) { - if ((flags1 & MEM_Str) == MEM_Str) { - mem_apply_numeric_type(pIn1); - testcase( flags3!=pIn3->flags); /* Possible if pIn1==pIn3 */ - flags3 = pIn3->flags; + if (mem_is_string(mem1) || mem_is_string(mem2)) { + if (mem_is_string(mem1)) { + mem_init(&tmp_mem1); + memcpy(&tmp_mem1, mem1, sizeof(*mem1)); + mem1 = &tmp_mem1; + mem_apply_numeric_type(mem1); } - if ((flags3 & MEM_Str) == MEM_Str) { - if (mem_apply_numeric_type(pIn3) != 0) { + if (mem_is_string(mem2)) { + mem_init(&tmp_mem2); + memcpy(&tmp_mem2, mem2, sizeof(*mem2)); + mem2 = &tmp_mem2; + if (mem_apply_numeric_type(mem2) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn3), + sql_value_to_diag_str(mem2), "numeric"); goto abort_due_to_error; } @@ -2335,27 +2300,29 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ /* Handle the common case of integer comparison here, as an * optimization, to avoid a call to sqlMemCompare() */ - if ((pIn1->flags & pIn3->flags & (MEM_Int | MEM_UInt)) != 0) { - if ((pIn1->flags & pIn3->flags & MEM_Int) != 0) { - if (pIn3->u.i > pIn1->u.i) + if (mem_is_integer(mem1) && mem_is_integer(mem2)) { + if (mem_is_neg_int(mem1) && + mem_is_neg_int(mem2)) { + if (mem2->u.i > mem1->u.i) res = +1; - else if (pIn3->u.i < pIn1->u.i) + else if (mem2->u.i < mem1->u.i) res = -1; else res = 0; goto compare_op; } - if ((pIn1->flags & pIn3->flags & MEM_UInt) != 0) { - if (pIn3->u.u > pIn1->u.u) + if (mem_is_pos_int(mem1) && + mem_is_pos_int(mem2)) { + if (mem2->u.u > mem1->u.u) res = +1; - else if (pIn3->u.u < pIn1->u.u) + else if (mem2->u.u < mem1->u.u) res = -1; else res = 0; goto compare_op; } - if ((pIn1->flags & MEM_UInt) != 0 && - (pIn3->flags & MEM_Int) != 0) { + if (mem_is_pos_int(mem1) && + mem_is_neg_int(mem2)) { res = -1; goto compare_op; } @@ -2363,26 +2330,22 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ goto compare_op; } } else if (type == FIELD_TYPE_STRING) { - if ((flags1 & MEM_Str) == 0 && - (flags1 & (MEM_Int | MEM_UInt | MEM_Real)) != 0) { - testcase( pIn1->flags & MEM_Int); - testcase( pIn1->flags & MEM_Real); - sqlVdbeMemStringify(pIn1); - testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn)); - flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); - assert(pIn1!=pIn3); + if (mem_is_number(mem1)) { + mem_init(&tmp_mem1); + memcpy(&tmp_mem1, mem1, sizeof(*mem1)); + mem1 = &tmp_mem1; + sqlVdbeMemStringify(mem1); + assert(mem1!=mem2); } - if ((flags3 & MEM_Str) == 0 && - (flags3 & (MEM_Int | MEM_UInt | MEM_Real)) != 0) { - testcase( pIn3->flags & MEM_Int); - testcase( pIn3->flags & MEM_Real); - sqlVdbeMemStringify(pIn3); - testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn)); - flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask); + if (mem_is_number(mem2)) { + mem_init(&tmp_mem2); + memcpy(&tmp_mem2, mem2, sizeof(*mem2)); + mem2 = &tmp_mem2; + sqlVdbeMemStringify(mem2); } } assert(pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0); - res = sqlMemCompare(pIn3, pIn1, pOp->p4.pColl); + res = sqlMemCompare(mem2, mem1, pOp->p4.pColl); } compare_op: switch( pOp->opcode) { @@ -2394,14 +2357,6 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ default: res2 = res>=0; break; } - /* Undo any changes made by mem_apply_type() to the input registers. */ - assert((pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn)); - pIn1->flags = flags1; - pIn1->field_type = ft_p1; - assert((pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn)); - pIn3->flags = flags3; - pIn3->field_type = ft_p3; - if (pOp->p5 & SQL_STOREP2) { iCompare = res; res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */ @@ -2585,9 +2540,9 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */ int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ pIn1 = &aMem[pOp->p1]; - if (pIn1->flags & MEM_Null) { + if (mem_is_null(pIn1)) { v1 = 2; - } else if ((pIn1->flags & MEM_Bool) != 0) { + } else if (mem_is_bool(pIn1)) { v1 = pIn1->u.b; } else { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, @@ -2595,9 +2550,9 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */ goto abort_due_to_error; } pIn2 = &aMem[pOp->p2]; - if (pIn2->flags & MEM_Null) { + if (mem_is_null(pIn2)) { v2 = 2; - } else if ((pIn2->flags & MEM_Bool) != 0) { + } else if (mem_is_bool(pIn2)) { v2 = pIn2->u.b; } else { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, @@ -2628,8 +2583,8 @@ case OP_Not: { /* same as TK_NOT, in1, out2 */ pIn1 = &aMem[pOp->p1]; pOut = vdbe_prepare_null_out(p, pOp->p2); pOut->field_type = FIELD_TYPE_BOOLEAN; - if ((pIn1->flags & MEM_Null)==0) { - if ((pIn1->flags & MEM_Bool) == 0) { + if (!mem_is_null(pIn1)) { + if (!mem_is_bool(pIn1)) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_to_diag_str(pIn1), "boolean"); goto abort_due_to_error; @@ -2651,7 +2606,7 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ pOut = vdbe_prepare_null_out(p, pOp->p2); /* Force NULL be of type INTEGER. */ pOut->field_type = FIELD_TYPE_INTEGER; - if ((pIn1->flags & MEM_Null)==0) { + if (!mem_is_null(pIn1)) { int64_t i; bool is_neg; if (sqlVdbeIntValue(pIn1, &i, &is_neg) != 0) { @@ -2696,9 +2651,9 @@ case OP_If: /* jump, in1 */ case OP_IfNot: { /* jump, in1 */ int c; pIn1 = &aMem[pOp->p1]; - if (pIn1->flags & MEM_Null) { + if (mem_is_null(pIn1)) { c = pOp->p3; - } else if ((pIn1->flags & MEM_Bool) != 0) { + } else if (mem_is_bool(pIn1)) { c = pOp->opcode == OP_IfNot ? ! pIn1->u.b : pIn1->u.b; } else { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, @@ -2719,8 +2674,8 @@ case OP_IfNot: { /* jump, in1 */ */ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ pIn1 = &aMem[pOp->p1]; - VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2); - if ((pIn1->flags & MEM_Null)!=0) { + VdbeBranchTaken(mem_is_null(pIn1), 2); + if (mem_is_null(pIn1)) { goto jump_to_p2; } break; @@ -2733,8 +2688,8 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ */ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ pIn1 = &aMem[pOp->p1]; - VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); - if ((pIn1->flags & MEM_Null)==0) { + VdbeBranchTaken(!mem_is_null(pIn1), 2); + if (!mem_is_null(pIn1)) { goto jump_to_p2; } break; @@ -2788,7 +2743,7 @@ case OP_Column: { if (pC->eCurType==CURTYPE_PSEUDO) { assert(pC->uc.pseudoTableReg>0); pReg = &aMem[pC->uc.pseudoTableReg]; - assert(pReg->flags & MEM_Blob); + assert(mem_is_binary(pReg)); assert(memIsValid(pReg)); vdbe_field_ref_prepare_data(&pC->field_ref, pReg->z, pReg->n); @@ -2817,7 +2772,7 @@ case OP_Column: { if (vdbe_field_ref_fetch(&pC->field_ref, p2, pDest) != 0) goto abort_due_to_error; - if ((pDest->flags & MEM_Null) && + if (mem_is_null(pDest) && (uint32_t) p2 >= pC->field_ref.field_count && default_val_mem != NULL) { sqlVdbeMemShallowCopy(pDest, default_val_mem, MEM_Static); @@ -2968,21 +2923,16 @@ case OP_MakeRecord: { * routine. */ if (bIsEphemeral) { - if (sqlVdbeMemClearAndResize(pOut, tuple_size) != 0) + if (mem_set_bin(pOut, tuple, tuple_size, 0, false) != 0) goto abort_due_to_error; - pOut->flags = MEM_Blob; - pOut->n = tuple_size; - memcpy(pOut->z, tuple, tuple_size); region_truncate(region, used); } else { /* Allocate memory on the region for the tuple * to be passed to Tarantool. Before that, make * sure previously allocated memory has gone. */ - sqlVdbeMemRelease(pOut); - pOut->flags = MEM_Blob | MEM_Ephem; - pOut->n = tuple_size; - pOut->z = tuple; + if (mem_set_bin(pOut, tuple, tuple_size, MEM_Ephem, false) != 0) + goto abort_due_to_error; } assert(sqlVdbeCheckMemInvariants(pOut)); assert(pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor)); @@ -3260,8 +3210,7 @@ case OP_OpenTEphemeral: { if (space == NULL) goto abort_due_to_error; - aMem[pOp->p1].u.p = space; - aMem[pOp->p1].flags = MEM_Ptr; + mem_set_ptr(&aMem[pOp->p1], (void *)space); break; } @@ -3490,18 +3439,18 @@ case OP_SeekGT: { /* jump, in3 */ * the seek, so convert it. */ pIn3 = &aMem[int_field]; - if ((pIn3->flags & MEM_Null) != 0) + if (mem_is_null(pIn3)) goto skip_truncate; - if ((pIn3->flags & MEM_Str) != 0) + if (mem_is_string(pIn3)) mem_apply_numeric_type(pIn3); int64_t i; - if ((pIn3->flags & MEM_Int) == MEM_Int) { + if (mem_is_neg_int(pIn3)) { i = pIn3->u.i; is_neg = true; - } else if ((pIn3->flags & MEM_UInt) == MEM_UInt) { + } else if (mem_is_pos_int(pIn3)) { i = pIn3->u.u; is_neg = false; - } else if ((pIn3->flags & MEM_Real) == MEM_Real) { + } else if (mem_is_double(pIn3)) { if (pIn3->u.r > (double)INT64_MAX) i = INT64_MAX; else if (pIn3->u.r < (double)INT64_MIN) @@ -3519,8 +3468,8 @@ case OP_SeekGT: { /* jump, in3 */ /* If the P3 value could not be converted into an integer without * loss of information, then special processing is required... */ - if ((pIn3->flags & (MEM_Int | MEM_UInt)) == 0) { - if ((pIn3->flags & MEM_Real)==0) { + if (!mem_is_integer(pIn3)) { + if (!mem_is_double(pIn3)) { /* If the P3 value cannot be converted into any kind of a number, * then the seek is not possible, so jump to P2 */ @@ -3736,7 +3685,7 @@ case OP_Found: { /* jump, in3 */ } else { pFree = pIdxKey = sqlVdbeAllocUnpackedRecord(db, pC->key_def); if (pIdxKey==0) goto no_mem; - assert(pIn3->flags & MEM_Blob ); + assert(mem_is_binary(pIn3)); (void)ExpandBlob(pIn3); sqlVdbeRecordUnpackMsgpack(pC->key_def, pIn3->z, pIdxKey); @@ -3750,7 +3699,7 @@ case OP_Found: { /* jump, in3 */ * conflict */ for(ii=0; iinField; ii++) { - if (pIdxKey->aMem[ii].flags & MEM_Null) { + if (mem_is_null(&pIdxKey->aMem[ii])) { takeJump = 1; break; } @@ -3860,14 +3809,14 @@ case OP_FCopy: { /* out2 */ pIn1 = &aMem[pOp->p1]; } - if ((pOp->p3 & OPFLAG_NOOP_IF_NULL) && (pIn1->flags & MEM_Null)) { + if ((pOp->p3 & OPFLAG_NOOP_IF_NULL) && mem_is_null(pIn1)) { pOut = vdbe_prepare_null_out(p, pOp->p2); } else { assert(memIsValid(pIn1)); - assert((pIn1->flags & (MEM_Int | MEM_UInt)) != 0); + assert(mem_is_integer(pIn1)); pOut = vdbe_prepare_null_out(p, pOp->p2); - mem_set_int(pOut, pIn1->u.i, pIn1->flags == MEM_Int); + mem_set_int(pOut, pIn1->u.i, mem_is_neg_int(pIn1)); pOut->field_type = pIn1->field_type; } break; @@ -3999,7 +3948,7 @@ case OP_SorterData: { assert(isSorter(pC)); if (sqlVdbeSorterRowkey(pC, pOut) != 0) goto abort_due_to_error; - assert(pOut->flags & MEM_Blob); + assert(mem_is_binary(pOut)); assert(pOp->p1>=0 && pOp->p1nCursor); p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE; break; @@ -4357,7 +4306,7 @@ case OP_SorterInsert: { /* in2 */ assert(cursor != NULL); assert(isSorter(cursor)); pIn2 = &aMem[pOp->p2]; - assert((pIn2->flags & MEM_Blob) != 0); + assert(mem_is_binary(pIn2)); if (ExpandBlob(pIn2) != 0 || sqlVdbeSorterWrite(cursor, pIn2) != 0) goto abort_due_to_error; @@ -4391,7 +4340,7 @@ case OP_SorterInsert: { /* in2 */ case OP_IdxReplace: case OP_IdxInsert: { pIn2 = &aMem[pOp->p1]; - assert((pIn2->flags & MEM_Blob) != 0); + assert(mem_is_binary(pIn2)); if (ExpandBlob(pIn2) != 0) goto abort_due_to_error; struct space *space; @@ -4436,7 +4385,7 @@ case OP_IdxInsert: { } if ((pOp->p5 & OPFLAG_NCHANGE) != 0) p->nChange++; - if (pOp->p3 > 0 && ((aMem[pOp->p3].flags) & MEM_Null) != 0) { + if (pOp->p3 > 0 && mem_is_null(&aMem[pOp->p3])) { assert(space->sequence != NULL); int64_t value; if (sequence_get_value(space->sequence, &value) != 0) @@ -4482,10 +4431,10 @@ case OP_Update: { assert(pOp->p4type == P4_SPACEPTR); struct Mem *key_mem = &aMem[pOp->p2]; - assert((key_mem->flags & MEM_Blob) != 0); + assert(mem_is_binary(key_mem)); struct Mem *upd_fields_mem = &aMem[pOp->p3]; - assert((upd_fields_mem->flags & MEM_Blob) != 0); + assert(mem_is_binary(upd_fields_mem)); uint32_t *upd_fields = (uint32_t *)upd_fields_mem->z; uint32_t upd_fields_cnt = upd_fields_mem->n / sizeof(uint32_t); @@ -4914,7 +4863,7 @@ case OP_Program: { /* jump */ * the trigger program. If this trigger has been fired before, then pRt * is already allocated. Otherwise, it must be initialized. */ - if ((pRt->flags&MEM_Frame)==0) { + if (!mem_is_frame(pRt)) { /* SubProgram.nMem is set to the number of memory cells used by the * program stored in SubProgram.aOp. As well as these, one memory * cell is required for each cursor used by the program. Set local @@ -4930,9 +4879,7 @@ case OP_Program: { /* jump */ if (!pFrame) { goto no_mem; } - sqlVdbeMemRelease(pRt); - pRt->flags = MEM_Frame; - pRt->u.pFrame = pFrame; + mem_set_frame(pRt, pFrame); pFrame->v = p; pFrame->nChildMem = nMem; @@ -4948,7 +4895,7 @@ case OP_Program: { /* jump */ pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem]; for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++) { - pMem->flags = MEM_Undefined; + mem_set_undefined(pMem); pMem->db = db; } } else { @@ -5054,8 +5001,8 @@ case OP_FkIfZero: { /* jump */ */ case OP_IfPos: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - assert((pIn1->flags & (MEM_Int | MEM_UInt)) != 0); - if ((pIn1->flags & MEM_UInt) != 0 && pIn1->u.u != 0) { + assert(mem_is_integer(pIn1)); + if (mem_is_pos_int(pIn1) && pIn1->u.u != 0) { assert(pOp->p3 >= 0); uint64_t res = pIn1->u.u - (uint64_t) pOp->p3; /* @@ -5089,8 +5036,8 @@ case OP_OffsetLimit: { /* in1, out2, in3 */ pIn3 = &aMem[pOp->p3]; pOut = vdbe_prepare_null_out(p, pOp->p2); - assert((pIn1->flags & MEM_UInt) != 0); - assert((pIn3->flags & MEM_UInt) != 0); + assert(mem_is_pos_int(pIn1)); + assert(mem_is_pos_int(pIn3)); uint64_t x = pIn1->u.u; uint64_t rhs = pIn3->u.u; bool unused; @@ -5113,7 +5060,7 @@ case OP_OffsetLimit: { /* in1, out2, in3 */ */ case OP_IfNotZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - assert((pIn1->flags & MEM_UInt) != 0); + assert(mem_is_pos_int(pIn1)); if (pIn1->u.u > 0) { pIn1->u.u--; goto jump_to_p2; @@ -5129,7 +5076,7 @@ case OP_IfNotZero: { /* jump, in1 */ */ case OP_DecrJumpZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - assert((pIn1->flags & MEM_UInt) != 0); + assert(mem_is_pos_int(pIn1)); if (pIn1->u.u > 0) pIn1->u.u--; if (pIn1->u.u == 0) goto jump_to_p2; @@ -5215,7 +5162,7 @@ case OP_AggStep: { #endif pMem->n++; - sqlVdbeMemInit(&t, db, MEM_Null); + mem_init(&t); pCtx->pOut = &t; pCtx->is_aborted = false; pCtx->skipFlag = 0; @@ -5226,7 +5173,7 @@ case OP_AggStep: { sqlVdbeMemRelease(&t); goto abort_due_to_error; } - assert(t.flags==MEM_Null); + assert(mem_is_null(&t)); if (pCtx->skipFlag) { assert(pOp[-1].opcode==OP_CollSeq); i = pOp[-1].p1; @@ -5252,7 +5199,7 @@ case OP_AggFinal: { Mem *pMem; assert(pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor)); pMem = &aMem[pOp->p1]; - assert((pMem->flags & ~(MEM_Null|MEM_Agg))==0); + assert(mem_is_null(pMem) || (pMem->flags & MEM_Agg) != 0); if (sql_vdbemem_finalize(pMem, pOp->p4.func) != 0) goto abort_due_to_error; UPDATE_MAX_BLOBSIZE(pMem); @@ -5354,11 +5301,11 @@ case OP_Init: { /* jump */ */ case OP_IncMaxid: { assert(pOp->p1 > 0); - pOut = vdbe_prepare_null_out(p, pOp->p1); - - if (tarantoolsqlIncrementMaxid(&pOut->u.u) != 0) + pOut = &p->aMem[pOp->p1]; + uint64_t id; + if (tarantoolsqlIncrementMaxid(&id) != 0) goto abort_due_to_error; - pOut->flags = MEM_UInt; + mem_set_u64(pOut, id); break; } @@ -5379,7 +5326,7 @@ case OP_SetSession: { struct session_setting *setting = &session_settings[sid]; switch (setting->field_type) { case FIELD_TYPE_BOOLEAN: { - if ((pIn1->flags & MEM_Bool) == 0) + if (!mem_is_bool(pIn1)) goto invalid_type; bool value = pIn1->u.b; size_t size = mp_sizeof_bool(value); @@ -5390,7 +5337,7 @@ case OP_SetSession: { break; } case FIELD_TYPE_STRING: { - if ((pIn1->flags & MEM_Str) == 0) + if (!mem_is_string(pIn1)) goto invalid_type; const char *str = pIn1->z; uint32_t size = mp_sizeof_str(pIn1->n); diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h index 7205f1af3..3c4cf08fb 100644 --- a/src/box/sql/vdbeInt.h +++ b/src/box/sql/vdbeInt.h @@ -484,6 +484,30 @@ void sqlVdbeMemMove(Mem *, Mem *); int sqlVdbeMemNulTerminate(Mem *); int sqlVdbeMemSetStr(Mem *, const char *, int, u8, void (*)(void *)); +/** + * Initialize a new mem. After initializing the mem will hold a NULL value. + * + * @param mem VDBE memory register to initialize. + */ +void +mem_init(struct Mem *mem); + +/** + * Set VDBE memory register as NULL. + * + * @param mem VDBE memory register to update. + */ +void +mem_set_null(struct Mem *mem); + +/** + * Set VDBE memory register as Undefined. MEM is not cleared prior to that. + * + * @param mem VDBE memory register to update. + */ +void +mem_set_undefined(struct Mem *mem); + void mem_set_bool(struct Mem *mem, bool value); @@ -495,6 +519,15 @@ mem_set_bool(struct Mem *mem, bool value); void mem_set_ptr(struct Mem *mem, void *ptr); +/** + * Set VDBE memory register as frame. + * + * @param mem VDBE memory register to update. + * @param frame Frame to set. + */ +void +mem_set_frame(struct Mem *mem, struct VdbeFrame *frame); + /** * Set integer value. Depending on its sign MEM_Int (in case * of negative value) or MEM_UInt flag is set. @@ -517,6 +550,177 @@ mem_set_int(struct Mem *mem, int64_t value, bool is_neg); void mem_set_double(struct Mem *mem, double value); +/** + * Set VDBE memory register as string. If is_null_terminated is true then + * sizeof(value) should be len + 1, otherwise sizeof(value) == len. If + * alloc_type is either MEM_Static or MEM_Ephem, then no more allocation, + * deallocation, or copying is required. In case it is MEM_Dyn, no need to + * allocate and copy, but MEM should be freed each time MEM changes or + * destroyed. If it is 0, the entire string should be copied into newly + * allocated memory, which should be freed when the memory is destroyed. + * However, in this case, there is no need to free memory every time the MEM + * changes. It should be deallocated in case this MEM is set a new string or + * binary string with alloc_type 0. + * + * @param mem VDBE memory register to update. + * @param value String to set. + * @param len Length of the string. + * @param alloc_type Type of allocation of binary string. + * @param is_null_terminated True if string is NULL-terminated. + */ +int +mem_set_str(struct Mem *mem, char *value, uint32_t len, int alloc_type, + bool is_null_terminated); + +/** + * Set VDBE memory register as binary. If is_zero is true then binary received + * from this MEM may have length more than given size, however this is done + * outside of this MEM. If alloc_type is either MEM_Static or MEM_Ephem, then no + * more allocation, deallocation, or copying is required. In case it is MEM_Dyn, + * no need to allocate and copy, but MEM should be freed each time MEM changes + * or destroyed. If it is 0, the entire binary string should be copied into + * newly allocated memory, which should be freed when the memory is destroyed. + * However, in this case, there is no need to free memory every time the MEM + * changes. It should be deallocated in case this MEM is set a new string or + * binary string with alloc_type 0. + * + * @param mem VDBE memory register to update. + * @param value Binary string to set. + * @param len Length of the string. + * @param alloc_type Type of allocation of binary string. + * @param is_zero True if binary string may be expanded with zeroes at the end. + */ +int +mem_set_bin(struct Mem *mem, char *value, uint32_t size, int alloc_type, + bool is_zero); + +/** + * Set VDBE memory register as MAP. See @a mem_set_bin with is_zero = 0 for + * more. + * + * @param mem VDBE memory register to update. + * @param value Binary string that contains msgpack with type MP_MAP to set. + * @param len Length of the binary string. + * @param alloc_type Type of allocation of binary string. + */ +int +mem_set_map(struct Mem *mem, char *value, uint32_t size, int alloc_type); + +/** + * Set VDBE memory register as ARRAY. See @a mem_set_bin with is_zero = 0 for + * more. + * + * @param mem VDBE memory register to update. + * @param value Binary string that contains msgpack with type MP_ARRAY to set. + * @param len Length of the binary string. + * @param alloc_type Type of allocation of binary string. + */ +int +mem_set_array(struct Mem *mem, char *value, uint32_t size, int alloc_type); + +static inline bool +mem_is_null(const struct Mem *mem) +{ + return (mem->flags & MEM_PURE_TYPE_MASK) == MEM_Null; +} + +static inline bool +mem_is_undefined(const struct Mem *mem) +{ + return mem->flags == MEM_Undefined; +} + +static inline bool +mem_is_frame(const struct Mem *mem) +{ + return mem->flags == MEM_Frame; +} + +static inline bool +mem_is_neg_int(const struct Mem *mem) +{ + return (mem->flags & MEM_PURE_TYPE_MASK) == MEM_Int; +} + +static inline bool +mem_is_pos_int(const struct Mem *mem) +{ + return (mem->flags & MEM_PURE_TYPE_MASK) == MEM_UInt; +} + +static inline bool +mem_is_integer(const struct Mem *mem) +{ + return mem_is_neg_int(mem) || mem_is_pos_int(mem); +} + +static inline bool +mem_is_double(const struct Mem *mem) +{ + return (mem->flags & MEM_PURE_TYPE_MASK) == MEM_Real; +} + +static inline bool +mem_is_number(const struct Mem *mem) +{ + return mem_is_integer(mem) || mem_is_double(mem); +} + +static inline bool +mem_is_string(const struct Mem *mem) +{ + return (mem->flags & MEM_PURE_TYPE_MASK) == MEM_Str; +} + +static inline bool +mem_is_binary(const struct Mem *mem) +{ + return (mem->flags & MEM_PURE_TYPE_MASK) == MEM_Blob; +} + +static inline bool +mem_is_varstring(const struct Mem *mem) +{ + return mem_is_string(mem) || mem_is_binary(mem); +} + +static inline bool +mem_is_map(const struct Mem *mem) +{ + return mem_is_binary(mem) && ((mem->flags & MEM_Subtype) != 0) && + mem->subtype == SQL_SUBTYPE_MSGPACK && + mp_typeof(*mem->z) == MP_MAP; +} + +static inline bool +mem_is_array(const struct Mem *mem) +{ + return mem_is_binary(mem) && ((mem->flags & MEM_Subtype) != 0) && + mem->subtype == SQL_SUBTYPE_MSGPACK && + mp_typeof(*mem->z) == MP_ARRAY; +} + +static inline bool +mem_is_bool(const struct Mem *mem) +{ + return (mem->flags & MEM_PURE_TYPE_MASK) == MEM_Bool; +} + +static inline bool +mems_have_same_type(const struct Mem *mem1, const struct Mem *mem2) +{ + return (mem1->flags & MEM_PURE_TYPE_MASK) == + (mem2->flags & MEM_PURE_TYPE_MASK); +} + +/** + * Cast MEM to varbinary according to explicit cast rules. + * + * @param mem VDBE memory register to convert. + */ +int +mem_convert_to_binary(struct Mem *mem); + void sqlVdbeMemInit(Mem *, sql *, u32); void sqlVdbeMemSetNull(Mem *); void sqlVdbeMemSetZeroBlob(Mem *, int); diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c index 7c59ef83f..22e87ec46 100644 --- a/src/box/sql/vdbeapi.c +++ b/src/box/sql/vdbeapi.c @@ -111,7 +111,7 @@ sql_clear_bindings(sql_stmt * pStmt) Vdbe *p = (Vdbe *) pStmt; for (i = 0; i < p->nVar; i++) { sqlVdbeMemRelease(&p->aVar[i]); - p->aVar[i].flags = MEM_Null; + mem_set_null(&p->aVar[i]); } return rc; } @@ -130,12 +130,12 @@ const void * sql_value_blob(sql_value * pVal) { Mem *p = (Mem *) pVal; - if (p->flags & (MEM_Blob | MEM_Str)) { + if (mem_is_varstring(p)) { if (ExpandBlob(p) != 0) { - assert(p->flags == MEM_Null && p->z == 0); + assert(mem_is_null(p) && p->z == NULL); return 0; } - p->flags |= MEM_Blob; + mem_convert_to_binary(p); return p->n ? p->z : 0; } else { return sql_value_text(pVal); @@ -232,7 +232,7 @@ sql_value_dup(const sql_value * pOrig) memcpy(pNew, pOrig, MEMCELLSIZE); pNew->flags &= ~MEM_Dyn; pNew->db = 0; - if (pNew->flags & (MEM_Str | MEM_Blob)) { + if (mem_is_varstring(pNew)) { pNew->flags &= ~(MEM_Static | MEM_Dyn); pNew->flags |= MEM_Ephem; if (sqlVdbeMemMakeWriteable(pNew) != 0) { @@ -588,39 +588,17 @@ sql_data_count(sql_stmt * pStmt) 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, + static struct Mem nullMem __attribute__ ((aligned(8))); +#else + static struct Mem nullMem; #endif - }; - return &nullMem; + static struct Mem *null_mem_ptr = NULL; + if (null_mem_ptr == NULL) { + mem_init(&nullMem); + null_mem_ptr = &nullMem; + } + return null_mem_ptr; } /* @@ -879,8 +857,7 @@ vdbeUnbind(Vdbe * p, int i) i--; pVar = &p->aVar[i]; sqlVdbeMemRelease(pVar); - pVar->flags = MEM_Null; - pVar->field_type = field_type_MAX; + mem_set_null(pVar); return 0; } diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c index 5c2706e49..02e8d90d7 100644 --- a/src/box/sql/vdbeaux.c +++ b/src/box/sql/vdbeaux.c @@ -1108,18 +1108,18 @@ displayP4(Op * pOp, char *zTemp, int nTemp) } case P4_MEM:{ Mem *pMem = pOp->p4.pMem; - if (pMem->flags & MEM_Str) { + if (mem_is_string(pMem)) { zP4 = pMem->z; - } else if (pMem->flags & MEM_Int) { + } else if (mem_is_neg_int(pMem)) { sqlXPrintf(&x, "%lld", pMem->u.i); - } else if (pMem->flags & MEM_UInt) { + } else if (mem_is_pos_int(pMem)) { sqlXPrintf(&x, "%llu", pMem->u.u); - } else if (pMem->flags & MEM_Real) { + } else if (mem_is_double(pMem)) { sqlXPrintf(&x, "%.16g", pMem->u.r); - } else if (pMem->flags & MEM_Null) { + } else if (mem_is_null(pMem)) { zP4 = "NULL"; } else { - assert(pMem->flags & MEM_Blob); + assert(mem_is_binary(pMem)); zP4 = "(binary string)"; } break; @@ -1198,17 +1198,13 @@ sqlVdbePrintOp(FILE * pOut, int pc, Op * pOp) * Initialize an array of N Mem element. */ static void -initMemArray(Mem * p, int N, sql * db, u32 flags) +initMemArray(Mem *p, int N, bool is_undefined) { - 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++; + for (int i = 0; i < N; ++i) { + struct Mem *mem = &p[i]; + mem_init(mem); + if (is_undefined) + mem_set_undefined(mem); } } @@ -1239,16 +1235,14 @@ releaseMemArray(Mem * p, int N) */ testcase(p->flags & MEM_Agg); testcase(p->flags & MEM_Dyn); - testcase(p->flags & MEM_Frame); - if (p-> - flags & (MEM_Agg | MEM_Dyn | MEM_Frame)) { + if (p->flags & (MEM_Agg | MEM_Dyn) || mem_is_frame(p)) { sqlVdbeMemRelease(p); } else if (p->szMalloc) { sqlDbFree(db, p->zMalloc); p->szMalloc = 0; } - p->flags = MEM_Undefined; + mem_set_undefined(p); } while ((++p) < pEnd); } } @@ -1322,7 +1316,7 @@ sqlVdbeList(Vdbe * p) */ assert(p->nMem > 9); pSub = &p->aMem[9]; - if (pSub->flags & MEM_Blob) { + if (mem_is_binary(pSub)) { /* On the first call to sql_step(), pSub will hold a NULL. It is * initialized to a BLOB by the P4_SUBPROGRAM processing logic below */ @@ -1364,10 +1358,10 @@ sqlVdbeList(Vdbe * p) pMem++; - pMem->flags = MEM_Static | MEM_Str | MEM_Term; - pMem->z = (char *)sqlOpcodeName(pOp->opcode); /* Opcode */ + char *str = (char *)sqlOpcodeName(pOp->opcode); + uint32_t len = strlen(str); + mem_set_str(pMem, str, len, MEM_Static, true); assert(pMem->z != 0); - pMem->n = sqlStrlen30(pMem->z); pMem++; /* When an OP_Program opcode is encounter (the only opcode that has @@ -1382,13 +1376,25 @@ sqlVdbeList(Vdbe * p) if (apSub[j] == pOp->p4.pProgram) break; } - if (j == nSub && - sqlVdbeMemGrow(pSub, nByte, - nSub != 0) == 0) { - apSub = (SubProgram **) pSub->z; - apSub[nSub++] = pOp->p4.pProgram; - pSub->flags |= MEM_Blob; - pSub->n = nSub * sizeof(SubProgram *); + if (j == nSub) { + size_t svp = region_used(&fiber()->gc); + struct SubProgram **buf = (SubProgram **) + region_aligned_alloc(&fiber()->gc, + nByte, + alignof(struct SubProgram)); + if (buf == NULL) { + diag_set(OutOfMemory, nByte, + "region_aligned_alloc", + "buf"); + p->is_aborted = true; + return -1; + } + if (nSub > 0) + memcpy(buf, pSub->z, pSub->n); + buf[nSub++] = pOp->p4.pProgram; + mem_set_bin(pSub, (char *)buf, nByte, 0, + false); + region_truncate(&fiber()->gc, svp); } } } @@ -1402,41 +1408,39 @@ sqlVdbeList(Vdbe * p) mem_set_i64(pMem, pOp->p3); pMem++; - if (sqlVdbeMemClearAndResize(pMem, 256)) { - assert(p->db->mallocFailed); + size_t size = 256; + size_t svp = region_used(&fiber()->gc); + char *buf = (char *)region_alloc(&fiber()->gc, size); + if (buf == NULL) { + diag_set(OutOfMemory, size, "region_alloc", "buf"); + p->is_aborted = true; return -1; } - pMem->flags = MEM_Str | MEM_Term; - zP4 = displayP4(pOp, pMem->z, pMem->szMalloc); - - if (zP4 != pMem->z) { - pMem->n = 0; - sqlVdbeMemSetStr(pMem, zP4, -1, 1, 0); - } else { - assert(pMem->z != 0); - pMem->n = sqlStrlen30(pMem->z); - } + zP4 = displayP4(pOp, buf, size); + mem_set_str(pMem, zP4, strlen(zP4), 0, true); + region_truncate(&fiber()->gc, svp); pMem++; if (p->explain == 1) { - if (sqlVdbeMemClearAndResize(pMem, 4)) { - assert(p->db->mallocFailed); - return -1; - } - pMem->flags = MEM_Str | MEM_Term; - pMem->n = 2; - sql_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */ + char *str = (char *)tt_sprintf("%02hu", pOp->p5); + mem_set_str(pMem, str, strlen(str), 0, true); pMem++; #ifdef SQL_ENABLE_EXPLAIN_COMMENTS - if (sqlVdbeMemClearAndResize(pMem, 500)) { - assert(p->db->mallocFailed); + size = 512; + svp = region_used(&fiber()->gc); + buf = (char *)region_alloc(&fiber()->gc, size); + if (buf == NULL) { + diag_set(OutOfMemory, size, "region_alloc", + "buf"); + p->is_aborted = true; return -1; } - pMem->flags = MEM_Str | MEM_Term; - pMem->n = displayComment(pOp, zP4, pMem->z, 500); + displayComment(pOp, zP4, buf, size); + mem_set_str(pMem, buf, strlen(buf), 0, true); + region_truncate(&fiber()->gc, svp); #else - pMem->flags = MEM_Null; /* Comment */ + mem_set_null(pMem); #endif } @@ -1658,9 +1662,9 @@ sqlVdbeMakeReady(Vdbe * p, /* The VDBE */ } else { p->nCursor = nCursor; p->nVar = (ynVar) nVar; - initMemArray(p->aVar, nVar, db, MEM_Null); + initMemArray(p->aVar, nVar, false); p->nMem = nMem; - initMemArray(p->aMem, nMem, db, MEM_Undefined); + initMemArray(p->aMem, nMem, true); memset(p->apCsr, 0, nCursor * sizeof(VdbeCursor *)); } sqlVdbeRewind(p); @@ -1787,7 +1791,7 @@ Cleanup(Vdbe * p) assert(p->apCsr[i] == 0); if (p->aMem) { for (i = 0; i < p->nMem; i++) - assert(p->aMem[i].flags == MEM_Undefined); + assert(mem_is_undefined(&p->aMem[i])); } #endif @@ -2330,7 +2334,7 @@ sqlVdbeAllocUnpackedRecord(struct sql *db, struct key_def *key_def) return 0; p->aMem = (Mem *) & ((char *)p)[ROUND8(sizeof(UnpackedRecord))]; for (uint32_t i = 0; i < key_def->part_count + 1; ++i) - sqlVdbeMemInit(&p->aMem[i], sql_get(), MEM_Null); + mem_init(&p->aMem[i]); p->key_def = key_def; p->nField = key_def->part_count + 1; return p; @@ -2418,80 +2422,72 @@ sqlBlobCompare(const Mem * pB1, const Mem * pB2) 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 (mem_is_null(pMem1) || mem_is_null(pMem2)) + return (int)mem_is_null(pMem2) - (int)mem_is_null(pMem1); - if ((combined_flags & MEM_Bool) != 0) { - if ((f1 & f2 & MEM_Bool) != 0) { + if (mem_is_bool(pMem1) || mem_is_bool(pMem2)) { + if (mem_is_bool(pMem1) && mem_is_bool(pMem2)) { if (pMem1->u.b == pMem2->u.b) return 0; if (pMem1->u.b) return 1; return -1; } - if ((f2 & MEM_Bool) != 0) + if (mem_is_bool(pMem2)) 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 (mem_is_number(pMem1) || mem_is_number(pMem2)) { + if (mem_is_neg_int(pMem1) && mem_is_neg_int(pMem2)) { 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 (mem_is_pos_int(pMem1) && mem_is_pos_int(pMem2)) { 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 (mem_is_double(pMem1) && mem_is_double(pMem2)) { 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) { + if (mem_is_neg_int(pMem1)) { + if (mem_is_double(pMem2)) { return double_compare_nint64(pMem2->u.r, pMem1->u.i, -1); } else { return -1; } } - if ((f1 & MEM_UInt) != 0) { - if ((f2 & MEM_Real) != 0) { + if (mem_is_pos_int(pMem1)) { + if (mem_is_double(pMem2)) { return double_compare_uint64(pMem2->u.r, pMem1->u.u, -1); - } else if ((f2 & MEM_Int) != 0) { + } else if (mem_is_neg_int(pMem2)) { return +1; } else { return -1; } } - if ((f1 & MEM_Real) != 0) { - if ((f2 & MEM_Int) != 0) { + if (mem_is_double(pMem1)) { + if (mem_is_neg_int(pMem2)) { return double_compare_nint64(pMem1->u.r, pMem2->u.i, 1); - } else if ((f2 & MEM_UInt) != 0) { + } else if (mem_is_pos_int(pMem2)) { return double_compare_uint64(pMem1->u.r, pMem2->u.u, 1); } else { @@ -2504,11 +2500,11 @@ sqlMemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pColl) /* 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) { + if (mem_is_string(pMem1) || mem_is_string(pMem2)) { + if (!mem_is_string(pMem1)) { return 1; } - if ((f2 & MEM_Str) == 0) { + if (!mem_is_string(pMem2)) { return -1; } /* The collation sequence must be defined at this point, even if @@ -2595,7 +2591,7 @@ sqlVdbeGetBoundValue(Vdbe * v, int iVar, u8 aff) assert(iVar > 0); if (v) { Mem *pMem = &v->aVar[iVar - 1]; - if (0 == (pMem->flags & MEM_Null)) { + if (!mem_is_null(pMem)) { sql_value *pRet = sqlValueNew(v->db); if (pRet) { sqlVdbeMemCopy((Mem *) pRet, pMem); @@ -2614,6 +2610,7 @@ sqlVdbeCompareMsgpack(const char **key1, const char *aKey1 = *key1; Mem *pKey2 = unpacked->aMem + key2_idx; Mem mem1; + mem_init(&mem1); int rc = 0; switch (mp_typeof(*aKey1)) { default:{ @@ -2622,35 +2619,35 @@ sqlVdbeCompareMsgpack(const char **key1, break; } case MP_NIL:{ - rc = -((pKey2->flags & MEM_Null) == 0); + rc = -(int)!mem_is_null(pKey2); mp_decode_nil(&aKey1); break; } case MP_BOOL:{ mem1.u.b = mp_decode_bool(&aKey1); - if ((pKey2->flags & MEM_Bool) != 0) { + if (mem_is_bool(pKey2)) { if (mem1.u.b != pKey2->u.b) rc = mem1.u.b ? 1 : -1; } else { - rc = (pKey2->flags & MEM_Null) != 0 ? 1 : -1; + rc = mem_is_null(pKey2) ? 1 : -1; } break; } case MP_UINT:{ mem1.u.u = mp_decode_uint(&aKey1); - if ((pKey2->flags & MEM_Int) != 0) { + if (mem_is_neg_int(pKey2)) { rc = +1; - } else if ((pKey2->flags & MEM_UInt) != 0) { + } else if (mem_is_pos_int(pKey2)) { 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) { + } else if (mem_is_double(pKey2)) { rc = double_compare_uint64(pKey2->u.r, mem1.u.u, -1); - } else if ((pKey2->flags & MEM_Null) != 0) { + } else if (mem_is_null(pKey2)) { rc = 1; - } else if ((pKey2->flags & MEM_Bool) != 0) { + } else if (mem_is_bool(pKey2)) { rc = 1; } else { rc = -1; @@ -2659,20 +2656,20 @@ sqlVdbeCompareMsgpack(const char **key1, } case MP_INT:{ mem1.u.i = mp_decode_int(&aKey1); - if ((pKey2->flags & MEM_UInt) != 0) { + if (mem_is_pos_int(pKey2)) { rc = -1; - } else if ((pKey2->flags & MEM_Int) != 0) { + } else if (mem_is_neg_int(pKey2)) { 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) { + } else if (mem_is_double(pKey2)) { rc = double_compare_nint64(pKey2->u.r, mem1.u.i, -1); - } else if ((pKey2->flags & MEM_Null) != 0) { + } else if (mem_is_null(pKey2)) { rc = 1; - } else if ((pKey2->flags & MEM_Bool) != 0) { + } else if (mem_is_bool(pKey2)) { rc = 1; } else { rc = -1; @@ -2686,21 +2683,21 @@ sqlVdbeCompareMsgpack(const char **key1, case MP_DOUBLE:{ mem1.u.r = mp_decode_double(&aKey1); do_float: - if ((pKey2->flags & MEM_Int) != 0) { + if (mem_is_neg_int(pKey2)) { rc = double_compare_nint64(mem1.u.r, pKey2->u.i, 1); - } else if (pKey2->flags & MEM_UInt) { + } else if (mem_is_pos_int(pKey2)) { rc = double_compare_uint64(mem1.u.r, pKey2->u.u, 1); - } else if (pKey2->flags & MEM_Real) { + } else if (mem_is_double(pKey2)) { 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) { + } else if (mem_is_null(pKey2)) { rc = 1; - } else if ((pKey2->flags & MEM_Bool) != 0) { + } else if (mem_is_bool(pKey2)) { rc = 1; } else { rc = -1; @@ -2708,31 +2705,35 @@ sqlVdbeCompareMsgpack(const char **key1, break; } case MP_STR:{ - if (pKey2->flags & MEM_Str) { + if (mem_is_string(pKey2)) { struct key_def *key_def = unpacked->key_def; - mem1.n = mp_decode_strl(&aKey1); - mem1.z = (char *)aKey1; - aKey1 += mem1.n; + uint32_t size = mp_decode_strl(&aKey1); + char *val = (char *)aKey1; + aKey1 += size; struct coll *coll = key_def->parts[key2_idx].coll; if (coll != NULL) { - mem1.flags = MEM_Str; + mem_set_str(&mem1, val, size, MEM_Ephem, + false); rc = vdbeCompareMemString(&mem1, pKey2, coll); } else { + mem_set_bin(&mem1, val, size, MEM_Ephem, + false); goto do_bin_cmp; } } else { - rc = (pKey2->flags & MEM_Blob) ? -1 : +1; + rc = mem_is_binary(pKey2) ? -1 : +1; } break; } case MP_BIN:{ - mem1.n = mp_decode_binl(&aKey1); - mem1.z = (char *)aKey1; - aKey1 += mem1.n; + uint32_t size = mp_decode_binl(&aKey1); + mem_set_bin(&mem1, (char *)aKey1, size, MEM_Ephem, + false); + aKey1 += size; do_blob: - if (pKey2->flags & MEM_Blob) { + if (mem_is_binary(pKey2)) { if (pKey2->flags & MEM_Zero) { if (!isAllZero ((const char *)mem1.z, mem1.n)) { @@ -2804,48 +2805,39 @@ vdbe_decode_msgpack_into_mem(const char *buf, struct Mem *mem, uint32_t *len) } case MP_NIL: { mp_decode_nil(&buf); - mem->flags = MEM_Null; + mem_set_null(mem); break; } case MP_BOOL: { - mem->u.b = mp_decode_bool(&buf); - mem->flags = MEM_Bool; + mem_set_bool(mem, mp_decode_bool(&buf)); break; } case MP_UINT: { - uint64_t v = mp_decode_uint(&buf); - mem->u.u = v; - mem->flags = MEM_UInt; + mem_set_u64(mem, mp_decode_uint(&buf)); break; } case MP_INT: { - mem->u.i = mp_decode_int(&buf); - mem->flags = MEM_Int; + mem_set_i64(mem, mp_decode_int(&buf)); break; } case MP_STR: { - /* XXX u32->int */ - mem->n = (int) mp_decode_strl(&buf); - mem->flags = MEM_Str | MEM_Ephem; -install_blob: - mem->z = (char *)buf; - buf += mem->n; + uint32_t len = mp_decode_strl(&buf); + mem_set_str(mem, (char *)buf, len, MEM_Ephem, false); + buf += len; break; } case MP_BIN: { - /* XXX u32->int */ - mem->n = (int) mp_decode_binl(&buf); - mem->flags = MEM_Blob | MEM_Ephem; - goto install_blob; + uint32_t size = mp_decode_binl(&buf); + mem_set_bin(mem, (char *)buf, size, MEM_Ephem, false); + buf += size; + break; } case MP_FLOAT: { - mem->u.r = mp_decode_float(&buf); - mem->flags = sqlIsNaN(mem->u.r) ? MEM_Null : MEM_Real; + mem_set_double(mem, mp_decode_float(&buf)); break; } case MP_DOUBLE: { - mem->u.r = mp_decode_double(&buf); - mem->flags = sqlIsNaN(mem->u.r) ? MEM_Null : MEM_Real; + mem_set_double(mem, mp_decode_double(&buf)); break; } } @@ -2872,10 +2864,9 @@ sqlVdbeRecordUnpackMsgpack(struct key_def *key_def, /* Information about the rec vdbe_decode_msgpack_into_mem(zParse, pMem, &sz); if (sz == 0) { /* MsgPack array, map or ext. Treat as blob. */ - pMem->z = (char *)zParse; + char *buf = (char *)zParse; mp_next(&zParse); - pMem->n = zParse - pMem->z; - pMem->flags = MEM_Blob | MEM_Ephem; + mem_set_bin(pMem, buf, zParse - buf, MEM_Ephem, false); } else { zParse += sz; } diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c index 1101f7205..849d490b8 100644 --- a/src/box/sql/vdbemem.c +++ b/src/box/sql/vdbemem.c @@ -799,6 +799,29 @@ sqlValueSetNull(sql_value * p) sqlVdbeMemSetNull((Mem *) p); } +void +mem_init(struct Mem *mem) +{ + memset(mem, 0, sizeof(*mem)); + mem->db = sql_get(); + mem->flags = MEM_Null; + mem->field_type = field_type_MAX; +} + +void +mem_set_null(struct Mem *mem) +{ + sqlVdbeMemSetNull(mem); + mem->field_type = field_type_MAX; +} + +void +mem_set_undefined(struct Mem *mem) +{ + mem->flags = MEM_Undefined; + mem->field_type = field_type_MAX; +} + void mem_set_ptr(struct Mem *mem, void *ptr) { @@ -807,6 +830,15 @@ mem_set_ptr(struct Mem *mem, void *ptr) mem->u.p = ptr; } +void +mem_set_frame(struct Mem *mem, struct VdbeFrame *frame) +{ + sqlVdbeMemSetNull(mem); + mem->u.pFrame = frame; + mem->flags = MEM_Frame; + mem->field_type = field_type_MAX; +} + /* * Delete any previous value and set the value to be a BLOB of length * n containing all zeros. @@ -880,6 +912,90 @@ mem_set_double(struct Mem *mem, double value) mem->field_type = FIELD_TYPE_DOUBLE; } +int +mem_set_str(struct Mem *mem, char *value, uint32_t len, int alloc_type, + bool is_null_terminated) +{ + sqlVdbeMemSetNull(mem); + if (alloc_type != 0) { + mem->z = value; + if (alloc_type == MEM_Dyn) + mem->xDel = SQL_DYNAMIC; + } else { + uint32_t size = is_null_terminated ? len + 1 : len; + if (sqlVdbeMemClearAndResize(mem, size) != 0) + return -1; + memcpy(mem->z, value, size); + } + mem->n = len; + mem->flags = MEM_Str | alloc_type; + if (is_null_terminated) + mem->flags |= MEM_Term; + mem->field_type = FIELD_TYPE_STRING; + return 0; +} + +int +mem_set_bin(struct Mem *mem, char *value, uint32_t size, int alloc_type, + bool is_zero) +{ + sqlVdbeMemSetNull(mem); + if (alloc_type != 0) { + mem->z = value; + if (alloc_type == MEM_Dyn) + mem->xDel = SQL_DYNAMIC; + } else { + if (sqlVdbeMemClearAndResize(mem, size) != 0) + return -1; + memcpy(mem->z, value, size); + } + mem->n = size; + mem->flags = MEM_Blob | alloc_type; + if (is_zero) + mem->flags |= MEM_Zero; + mem->field_type = FIELD_TYPE_VARBINARY; + return 0; +} + +int +mem_set_map(struct Mem *mem, char *value, uint32_t size, int alloc_type) +{ + assert(mp_typeof(*value) == MP_MAP); + if (mem_set_bin(mem, value, size, alloc_type, false) != 0) + return -1; + mem->subtype = SQL_SUBTYPE_MSGPACK; + mem->flags |= MEM_Subtype; + mem->field_type = FIELD_TYPE_MAP; + return 0; +} + +int +mem_set_array(struct Mem *mem, char *value, uint32_t size, int alloc_type) +{ + assert(mp_typeof(*value) == MP_ARRAY); + if (mem_set_bin(mem, value, size, alloc_type, false) != 0) + return -1; + mem->subtype = SQL_SUBTYPE_MSGPACK; + mem->flags |= MEM_Subtype; + mem->field_type = FIELD_TYPE_ARRAY; + return 0; +} + +int +mem_convert_to_binary(struct Mem *mem) +{ + if (mem_is_null(mem) || mem_is_binary(mem)) + return 0; + if (mem_is_string(mem)) { + mem->flags = (mem->flags & (MEM_Dyn | MEM_Static | MEM_Ephem)) | + MEM_Blob; + return 0; + } + diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_to_diag_str(mem), + "varbinary"); + return -1; +} + /* * Return true if the Mem object contains a TEXT or BLOB that is * too large - whose size exceeds SQL_MAX_LENGTH. diff --git a/src/box/sql/vdbesort.c b/src/box/sql/vdbesort.c index a2d681255..46759e591 100644 --- a/src/box/sql/vdbesort.c +++ b/src/box/sql/vdbesort.c @@ -2163,13 +2163,8 @@ sqlVdbeSorterRowkey(const VdbeCursor * pCsr, Mem * pOut) assert(pCsr->eCurType == CURTYPE_SORTER); pSorter = pCsr->uc.pSorter; pKey = vdbeSorterRowkey(pSorter, &nKey); - if (sqlVdbeMemClearAndResize(pOut, nKey)) { + if (mem_set_bin(pOut, pKey, nKey, 0, false) != 0) return -1; - } - pOut->n = nKey; - MemSetTypeFlag(pOut, MEM_Blob); - memcpy(pOut->z, pKey, nKey); - return 0; } @@ -2217,7 +2212,7 @@ sqlVdbeSorterCompare(const VdbeCursor * pCsr, /* Sorter cursor */ pKey = vdbeSorterRowkey(pSorter, &nKey); sqlVdbeRecordUnpackMsgpack(pCsr->key_def, pKey, r2); for (i = 0; i < nKeyCol; i++) { - if (r2->aMem[i].flags & MEM_Null) { + if (mem_is_null(&r2->aMem[i])) { *pRes = -1; return 0; } diff --git a/src/box/sql/vdbetrace.c b/src/box/sql/vdbetrace.c index 2ee9f668c..175cbc0a6 100644 --- a/src/box/sql/vdbetrace.c +++ b/src/box/sql/vdbetrace.c @@ -147,15 +147,15 @@ sqlVdbeExpandSql(Vdbe * p, /* The prepared statement being evaluated */ nextIndex = idx + 1; assert(idx > 0 && idx <= p->nVar); pVar = &p->aVar[idx - 1]; - if (pVar->flags & MEM_Null) { + if (mem_is_null(pVar)) { sqlStrAccumAppend(&out, "NULL", 4); - } else if (pVar->flags & MEM_Int) { + } else if (mem_is_neg_int(pVar)) { sqlXPrintf(&out, "%lld", pVar->u.i); - } else if (pVar->flags & MEM_UInt) { + } else if (mem_is_pos_int(pVar)) { sqlXPrintf(&out, "%llu", pVar->u.u); - } else if (pVar->flags & MEM_Real) { + } else if (mem_is_double(pVar)) { sqlXPrintf(&out, "%!.15g", pVar->u.r); - } else if (pVar->flags & MEM_Str) { + } else if (mem_is_string(pVar)) { int nOut; /* Number of bytes of the string text to include in output */ nOut = pVar->n; sqlXPrintf(&out, "'%.*q'", nOut, pVar->z); @@ -164,7 +164,7 @@ sqlVdbeExpandSql(Vdbe * p, /* The prepared statement being evaluated */ pVar->u.nZero); } else { int nOut; /* Number of bytes of the blob to include in output */ - assert(pVar->flags & MEM_Blob); + assert(mem_is_binary(pVar)); sqlStrAccumAppend(&out, "x'", 2); nOut = pVar->n; for (i = 0; i < nOut; i++) { -- 2.25.1