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 E41056BD2D; Tue, 13 Apr 2021 15:36:04 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org E41056BD2D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1618317364; bh=IqivLPO7om0Oi8xMpNtP/ea+XLuLUClYfLgCBPYDh3o=; h=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=KTB8FuKfLYhENUh0/JmoX3+MxjaHlN7yTu6W5LYqNDz+li5Y6Du/uYSBc7SxstWuh L8lu2toHC/b+2VYwYznx6YLtnnwSXBXFEN0CRM/FoVgCxJfN5FBwpeZPpzyHQ4dQMZ MDCK0j8NCwGj/ogT3qqWY9gAqUAKqvd9UpgF+VNw= 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 5F77B6BD29 for ; Tue, 13 Apr 2021 15:36:03 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 5F77B6BD29 Received: by smtpng1.m.smailru.net with esmtpa (envelope-from ) id 1lWIGw-0001oY-Am; Tue, 13 Apr 2021 15:36:02 +0300 Date: Tue, 13 Apr 2021 15:36:01 +0300 To: Vladislav Shpilevoy Message-ID: <20210413123601.GA108689@tarantool.org> References: <2ce8b15b5c1869dec995b4a9f348fe7649f6af47.1617984948.git.imeevma@gmail.com> <6128a7ea-7fb9-8fd6-dc99-0feeb4759e6c@tarantool.org> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <6128a7ea-7fb9-8fd6-dc99-0feeb4759e6c@tarantool.org> X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD92FFCB8E6708E74806859AC5FE18436AEED970E897805ADA4182A05F538085040FF3FE9392601B8F4F1C585BC041CEA418BB9E09A589EF37101708587A242FE99 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7BDF1FA55CCD598A3EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F79006376F127A835590024F8F08D7030A58E5ADC58D69EE07B14084F39EFFDF887939037866D6147AF826D8E8EF2917C4E3E922B0A5ABD179C3DCB0117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BF1175FABE1C0F9B6A471835C12D1D977C4224003CC8364762BB6847A3DEAEFB02EB15956EA79C166A417C69337E82CC275ECD9A6C639B01B78DA827A17800CE7F669735A3130CE6243847C11F186F3C59DAA53EE0834AAEE X-C1DE0DAB: 0D63561A33F958A54AB88D91CD6C53FED25E5CDDC107472CC0F3E8794D53F513D59269BC5F550898D99A6476B3ADF6B47008B74DF8BB9EF7333BD3B22AA88B938A852937E12ACA7502E6951B79FF9A3F410CA545F18667F91A7EA1CDA0B5A7A0 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D346840168BCAD8054E0C3F167402FD08A7114515C2B62ABF2A3800BF5A7121F939E04596306EBFA0D31D7E09C32AA3244C7DC5EB0F9E042B2D03BFA06BF7E3D6C63E8609A02908F271FACE5A9C96DEB163 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojnA7/qPBUIXGekaYvUKFi7w== X-Mailru-Sender: 689FA8AB762F73936BC43F508A06382227FC5D1B19F604CFC967B31DB4B0C72D83D72C36FC87018B9F80AB2734326CD2FB559BB5D741EB96352A0ABBE4FDA4210A04DAD6CC59E33667EA787935ED9F1B X-Mras: Ok Subject: Re: [Tarantool-patches] [PATCH v5 09/52] sql: introduce mem_str() 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: Mergen Imeev Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" Thank you for the review! My answer, diff and new patch below. On Sun, Apr 11, 2021 at 07:44:53PM +0200, Vladislav Shpilevoy wrote: > I appreciate the work you did here! > > > diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c > > index 79c3d60e0..b7e148422 100644 > > --- a/src/box/sql/vdbeaux.c > > +++ b/src/box/sql/vdbeaux.c > > @@ -1108,21 +1108,10 @@ displayP4(Op * pOp, char *zTemp, int nTemp) > > break; > > } > > case P4_MEM:{ > > - Mem *pMem = pOp->p4.pMem; > > - if (pMem->flags & MEM_Str) { > > - zP4 = pMem->z; > > - } else if (pMem->flags & MEM_Int) { > > - sqlXPrintf(&x, "%lld", pMem->u.i); > > - } else if (pMem->flags & MEM_UInt) { > > - sqlXPrintf(&x, "%llu", pMem->u.u); > > - } else if (pMem->flags & MEM_Real) { > > - sqlXPrintf(&x, "%.16g", pMem->u.r); > > - } else if (pMem->flags & MEM_Null) { > > - zP4 = "NULL"; > > - } else { > > - assert(pMem->flags & MEM_Blob); > > - zP4 = "(binary string)"; > > - } > > + const char *value = mem_str(pOp->p4.pMem); > > + uint32_t size = MIN((int)strlen(value), nTemp - 1); > > + memcpy(zP4, value, size); > > + zP4[size] = '\0'; > > 'x' stays in an invalid state now, because its counter nChar is > not updated. I would propose to use sqlStrAccumAppend/sqlStrAccumAppendAll > instead of memcpy and manual 0 termination. > > The same below in sqlVdbeExpandSql(). > Fixed. > > break; > > } > > case P4_INTARRAY:{ > > diff --git a/src/box/sql/vdbetrace.c b/src/box/sql/vdbetrace.c > > index e84bb3192..4ca56865d 100644 > > --- a/src/box/sql/vdbetrace.c > > +++ b/src/box/sql/vdbetrace.c > > @@ -147,33 +145,11 @@ sqlVdbeExpandSql(Vdbe * p, /* The prepared statement being evaluated */ > > zRawSql += nToken; > > nextIndex = idx + 1; > > assert(idx > 0 && idx <= p->nVar); > > - pVar = &p->aVar[idx - 1]; > > - if (pVar->flags & MEM_Null) { > > - sqlStrAccumAppend(&out, "NULL", 4); > > - } else if (pVar->flags & MEM_Int) { > > - sqlXPrintf(&out, "%lld", pVar->u.i); > > - } else if (pVar->flags & MEM_UInt) { > > - sqlXPrintf(&out, "%llu", pVar->u.u); > > - } else if (pVar->flags & MEM_Real) { > > - sqlXPrintf(&out, "%!.15g", pVar->u.r); > > - } else if (pVar->flags & MEM_Str) { > > - int nOut; /* Number of bytes of the string text to include in output */ > > - nOut = pVar->n; > > - sqlXPrintf(&out, "'%.*q'", nOut, pVar->z); > > - } else if (pVar->flags & MEM_Zero) { > > - sqlXPrintf(&out, "zeroblob(%d)", > > - pVar->u.nZero); > > - } else { > > - int nOut; /* Number of bytes of the blob to include in output */ > > - assert(pVar->flags & MEM_Blob); > > - sqlStrAccumAppend(&out, "x'", 2); > > - nOut = pVar->n; > > - for (i = 0; i < nOut; i++) { > > - sqlXPrintf(&out, "%02x", > > - pVar->z[i] & 0xff); > > - } > > - sqlStrAccumAppend(&out, "'", 1); > > - } > > + const char *value = mem_str(&p->aVar[idx - 1]); > > + uint32_t len = strlen(value); > > + uint32_t size = MIN(len, sizeof(zBase) - 1); > > + memcpy(zBase, value, size); > > + zBase[size] = '\0'; > > } > > } > > if (out.accError) > > Diff: diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c index b7e148422..907c9f5c6 100644 --- a/src/box/sql/vdbeaux.c +++ b/src/box/sql/vdbeaux.c @@ -1109,9 +1109,9 @@ displayP4(Op * pOp, char *zTemp, int nTemp) } case P4_MEM:{ const char *value = mem_str(pOp->p4.pMem); - uint32_t size = MIN((int)strlen(value), nTemp - 1); - memcpy(zP4, value, size); - zP4[size] = '\0'; + int len = strlen(value); + uint32_t size = MIN(len, nTemp - 1); + sqlStrAccumAppend(&x, value, size); break; } case P4_INTARRAY:{ diff --git a/src/box/sql/vdbetrace.c b/src/box/sql/vdbetrace.c index 4ca56865d..677de65e2 100644 --- a/src/box/sql/vdbetrace.c +++ b/src/box/sql/vdbetrace.c @@ -148,8 +148,7 @@ sqlVdbeExpandSql(Vdbe * p, /* The prepared statement being evaluated */ const char *value = mem_str(&p->aVar[idx - 1]); uint32_t len = strlen(value); uint32_t size = MIN(len, sizeof(zBase) - 1); - memcpy(zBase, value, size); - zBase[size] = '\0'; + sqlStrAccumAppend(&out, value, size); } } if (out.accError) New patch commit 3ef9df0c7c1a9947a63fd024b966690cc065d2b0 Author: Mergen Imeev Date: Tue Mar 2 13:52:11 2021 +0300 sql: introduce mem_str() This patch introduces mem_str() which allows to receive value of MEM as a string. Due to the limitations of static_alloc(), this function cannot be used to safely retrieve a value of MEM converted to string. This function is suitable for debugging, displaying the value in an error message, etc. Part of #5818 diff --git a/src/box/sql/func.c b/src/box/sql/func.c index 46814f341..99ce938d5 100644 --- a/src/box/sql/func.c +++ b/src/box/sql/func.c @@ -543,7 +543,7 @@ roundFunc(sql_context * context, int argc, sql_value ** argv) enum mp_type mp_type = sql_value_type(argv[0]); if (mp_type_is_bloblike(mp_type)) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(argv[0]), "numeric"); + mem_str(argv[0]), "numeric"); context->is_aborted = true; return; } @@ -685,7 +685,7 @@ randomBlob(sql_context * context, int argc, sql_value ** argv) UNUSED_PARAMETER(argc); if (mp_type_is_bloblike(sql_value_type(argv[0]))) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(argv[0]), "numeric"); + mem_str(argv[0]), "numeric"); context->is_aborted = true; return; } @@ -1577,7 +1577,7 @@ soundexFunc(sql_context * context, int argc, sql_value ** argv) enum mp_type mp_type = sql_value_type(argv[0]); if (mp_type_is_bloblike(mp_type)) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(argv[0]), "text"); + mem_str(argv[0]), "text"); context->is_aborted = true; return; } @@ -1650,7 +1650,7 @@ sum_step(struct sql_context *context, int argc, sql_value **argv) if (type != MP_DOUBLE && type != MP_INT && type != MP_UINT) { if (mem_apply_numeric_type(argv[0]) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(argv[0]), "number"); + mem_str(argv[0]), "number"); context->is_aborted = true; return; } diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c index 25f5f2f2d..a2a0fc33e 100644 --- a/src/box/sql/mem.c +++ b/src/box/sql/mem.c @@ -40,6 +40,39 @@ #include "lua/utils.h" #include "lua/msgpack.h" +enum { + BUF_SIZE = 32, +}; + +const char * +mem_str(const struct Mem *mem) +{ + char buf[BUF_SIZE]; + switch (mem->flags & MEM_PURE_TYPE_MASK) { + case MEM_Null: + return "NULL"; + case MEM_Str: + return tt_sprintf("%.*s", mem->n, mem->z); + case MEM_Int: + return tt_sprintf("%lld", mem->u.i); + case MEM_UInt: + return tt_sprintf("%llu", mem->u.u); + case MEM_Real: + sql_snprintf(BUF_SIZE, &buf[0], "%!.15g", mem->u.r); + return tt_sprintf("%s", buf); + case MEM_Blob: + if ((mem->flags & MEM_Subtype) == 0) + return "varbinary"; + assert(mem->subtype == SQL_SUBTYPE_MSGPACK); + return mp_str(mem->z); + case MEM_Bool: + return mem->u.b ? "TRUE" : "FALSE"; + default: + break; + } + return "unknown"; +} + static inline bool mem_has_msgpack_subtype(struct Mem *mem) { @@ -1592,18 +1625,6 @@ sqlValueText(sql_value * pVal) 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) { diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h index acc8ce054..7b9456426 100644 --- a/src/box/sql/mem.h +++ b/src/box/sql/mem.h @@ -87,6 +87,13 @@ struct Mem { */ #define MEMCELLSIZE offsetof(Mem,zMalloc) +/** + * Return a string that represent content of MEM. String is either allocated + * using static_alloc() of just a static variable. + */ +const char * +mem_str(const struct Mem *mem); + /* One or more of the following flags are set to indicate the validOK * representations of the value stored in the Mem struct. * @@ -358,15 +365,6 @@ 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 diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index e269857ea..ec3d23cb2 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -1265,12 +1265,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ } else { if (sqlVdbeRealValue(pIn1, &rA) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn1), "numeric"); + mem_str(pIn1), "numeric"); goto abort_due_to_error; } if (sqlVdbeRealValue(pIn2, &rB) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn2), "numeric"); + mem_str(pIn2), "numeric"); goto abort_due_to_error; } assert(((type1 | type2) & MEM_Real) != 0); @@ -1546,12 +1546,12 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ bool unused; if (sqlVdbeIntValue(pIn2, (int64_t *) &iA, &unused) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn2), "integer"); + mem_str(pIn2), "integer"); goto abort_due_to_error; } if (sqlVdbeIntValue(pIn1, (int64_t *) &iB, &unused) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn1), "integer"); + mem_str(pIn1), "integer"); goto abort_due_to_error; } op = pOp->opcode; @@ -1616,7 +1616,7 @@ case OP_MustBeInt: { /* jump, in1 */ if ((pIn1->flags & (MEM_Int | MEM_UInt)) == 0) { if (pOp->p2==0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn1), "integer"); + mem_str(pIn1), "integer"); goto abort_due_to_error; } else { goto jump_to_p2; @@ -1673,7 +1673,7 @@ case OP_Cast: { /* in1 */ UPDATE_MAX_BLOBSIZE(pIn1); if (rc == 0) break; - diag_set(ClientError, ER_SQL_TYPE_MISMATCH, sql_value_to_diag_str(pIn1), + diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(pIn1), field_type_strs[pOp->p2]); goto abort_due_to_error; } @@ -1846,7 +1846,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ if (mem_apply_numeric_type(pIn3) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn3), + mem_str(pIn3), "numeric"); goto abort_due_to_error; } @@ -2112,7 +2112,7 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */ v1 = pIn1->u.b; } else { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn1), "boolean"); + mem_str(pIn1), "boolean"); goto abort_due_to_error; } pIn2 = &aMem[pOp->p2]; @@ -2122,7 +2122,7 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */ v2 = pIn2->u.b; } else { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn2), "boolean"); + mem_str(pIn2), "boolean"); goto abort_due_to_error; } if (pOp->opcode==OP_And) { @@ -2152,7 +2152,7 @@ case OP_Not: { /* same as TK_NOT, in1, out2 */ if ((pIn1->flags & MEM_Null)==0) { if ((pIn1->flags & MEM_Bool) == 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn1), "boolean"); + mem_str(pIn1), "boolean"); goto abort_due_to_error; } mem_set_bool(pOut, ! pIn1->u.b); @@ -2177,7 +2177,7 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ bool is_neg; if (sqlVdbeIntValue(pIn1, &i, &is_neg) != 0) { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn1), "integer"); + mem_str(pIn1), "integer"); goto abort_due_to_error; } mem_set_i64(pOut, ~i); @@ -2223,7 +2223,7 @@ case OP_IfNot: { /* jump, in1 */ c = pOp->opcode == OP_IfNot ? ! pIn1->u.b : pIn1->u.b; } else { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn1), "boolean"); + mem_str(pIn1), "boolean"); goto abort_due_to_error; } VdbeBranchTaken(c!=0, 2); @@ -2403,7 +2403,7 @@ case OP_ApplyType: { continue; type_mismatch: diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn1), field_type_strs[type]); + mem_str(pIn1), field_type_strs[type]); goto abort_due_to_error; } break; @@ -3032,7 +3032,7 @@ case OP_SeekGT: { /* jump, in3 */ is_neg = i < 0; } else { diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_to_diag_str(pIn3), "integer"); + mem_str(pIn3), "integer"); goto abort_due_to_error; } iKey = i; diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c index 79c3d60e0..907c9f5c6 100644 --- a/src/box/sql/vdbeaux.c +++ b/src/box/sql/vdbeaux.c @@ -1108,21 +1108,10 @@ displayP4(Op * pOp, char *zTemp, int nTemp) break; } case P4_MEM:{ - Mem *pMem = pOp->p4.pMem; - if (pMem->flags & MEM_Str) { - zP4 = pMem->z; - } else if (pMem->flags & MEM_Int) { - sqlXPrintf(&x, "%lld", pMem->u.i); - } else if (pMem->flags & MEM_UInt) { - sqlXPrintf(&x, "%llu", pMem->u.u); - } else if (pMem->flags & MEM_Real) { - sqlXPrintf(&x, "%.16g", pMem->u.r); - } else if (pMem->flags & MEM_Null) { - zP4 = "NULL"; - } else { - assert(pMem->flags & MEM_Blob); - zP4 = "(binary string)"; - } + const char *value = mem_str(pOp->p4.pMem); + int len = strlen(value); + uint32_t size = MIN(len, nTemp - 1); + sqlStrAccumAppend(&x, value, size); break; } case P4_INTARRAY:{ diff --git a/src/box/sql/vdbetrace.c b/src/box/sql/vdbetrace.c index e84bb3192..677de65e2 100644 --- a/src/box/sql/vdbetrace.c +++ b/src/box/sql/vdbetrace.c @@ -97,8 +97,6 @@ sqlVdbeExpandSql(Vdbe * p, /* The prepared statement being evaluated */ int nextIndex = 1; /* Index of next ? host parameter */ int n; /* Length of a token prefix */ int nToken; /* Length of the parameter token */ - int i; /* Loop counter */ - Mem *pVar; /* Value of a host parameter */ StrAccum out; /* Accumulate the output here */ char zBase[100]; /* Initial working space */ @@ -147,33 +145,10 @@ sqlVdbeExpandSql(Vdbe * p, /* The prepared statement being evaluated */ zRawSql += nToken; nextIndex = idx + 1; assert(idx > 0 && idx <= p->nVar); - pVar = &p->aVar[idx - 1]; - if (pVar->flags & MEM_Null) { - sqlStrAccumAppend(&out, "NULL", 4); - } else if (pVar->flags & MEM_Int) { - sqlXPrintf(&out, "%lld", pVar->u.i); - } else if (pVar->flags & MEM_UInt) { - sqlXPrintf(&out, "%llu", pVar->u.u); - } else if (pVar->flags & MEM_Real) { - sqlXPrintf(&out, "%!.15g", pVar->u.r); - } else if (pVar->flags & MEM_Str) { - int nOut; /* Number of bytes of the string text to include in output */ - nOut = pVar->n; - sqlXPrintf(&out, "'%.*q'", nOut, pVar->z); - } else if (pVar->flags & MEM_Zero) { - sqlXPrintf(&out, "zeroblob(%d)", - pVar->u.nZero); - } else { - int nOut; /* Number of bytes of the blob to include in output */ - assert(pVar->flags & MEM_Blob); - sqlStrAccumAppend(&out, "x'", 2); - nOut = pVar->n; - for (i = 0; i < nOut; i++) { - sqlXPrintf(&out, "%02x", - pVar->z[i] & 0xff); - } - sqlStrAccumAppend(&out, "'", 1); - } + const char *value = mem_str(&p->aVar[idx - 1]); + uint32_t len = strlen(value); + uint32_t size = MIN(len, sizeof(zBase) - 1); + sqlStrAccumAppend(&out, value, size); } } if (out.accError)