From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 22FFF245B6 for ; Mon, 28 Jan 2019 11:39:53 -0500 (EST) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id K-Ms2-m1-trW for ; Mon, 28 Jan 2019 11:39:53 -0500 (EST) Received: from smtp58.i.mail.ru (smtp58.i.mail.ru [217.69.128.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 9F55620EF7 for ; Mon, 28 Jan 2019 11:39:52 -0500 (EST) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 12.2 \(3445.102.3\)) Subject: [tarantool-patches] Re: [PATCH 5/8] sql: replace field type with affinity for VDBE runtime From: "n.pettik" In-Reply-To: Date: Mon, 28 Jan 2019 19:39:50 +0300 Content-Transfer-Encoding: quoted-printable Message-Id: <93178595-D7AF-49DE-8273-0FBD18545D56@tarantool.org> References: <6e7623ee-fdcc-616f-6a99-20aff0e97f43@tarantool.org> Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org Cc: Vladislav Shpilevoy >>>> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c >>>> index 917e6e30b..c823c5a06 100644 >>>> --- a/src/box/sql/expr.c >>>> +++ b/src/box/sql/expr.c >>>> @@ -2858,8 +2858,11 @@ sqlite3CodeSubselect(Parse * pParse, = /* Parsing context */ >>>> jmpIfDynamic =3D -1; >>>> } >>>> r3 =3D = sqlite3ExprCodeTarget(pParse, pE2, r1); >>>> + char type =3D >>>> + = sql_affinity_to_field_type(affinity); >>>> sqlite3VdbeAddOp4(v, = OP_MakeRecord, r3, >>>> - 1, r2, = &affinity, 1); >>>> + 1, r2, &type, >>>> + 1); >>>=20 >>> 4. I do not understand. Is p4type of sqlite3VdbeAddOp4 of = OP_MakeRecord >>> a length of an affinity string, or a type of its allocation? Or = both? >> Both. p4type > 0 means that p4type bytes are copied to freshly = allocated memory >> and P4 set to DYNAMIC. sqlite3DbStrNDup() in vdbeChangeP4Full() = reserves >> one more byte for NULL termination. >=20 > I see now, that OP_MakeRecord uses only p4.z and does not touch = p4type, so it makes > no sense to pass here length of the field_type array. Please, use only = P4_STATIC and > P4_DYNAMIC. At this moment this opcode is mess: somewhere 0 is passed, = somewhere > P4_DYNAMIC, in other places length of a field_type array. Well, actually we can=E2=80=99t pass here neither of these args and = that=E2=80=99s why: STATIC and DYNAMIC indicate that memory which is passed through P4 argument will be copied to new chunk of memory: 985 void 986 sqlite3VdbeChangeP4(Vdbe * p, int addr, const char *zP4, int n) 987 { =20 =E2=80=A6 1008 if (n =3D=3D P4_INT32) { 1009 /* Note: this cast is safe, because the origin = data point was an int 1010 * that was cast to a (const char *). 1011 */ 1012 pOp->p4.i =3D SQLITE_PTR_TO_INT(zP4); 1013 pOp->p4type =3D P4_INT32; 1014 } if (n =3D=3D P4_BOOL) { 1015 pOp->p4.b =3D *(bool*)zP4; 1016 pOp->p4type =3D P4_BOOL; 1017 } else { 1018 assert(n < 0); 1019 pOp->p4.p =3D (void *)zP4; 1020 pOp->p4type =3D (signed char)n; 1021 } 1022 } Both P4_STATIC and P4_DYNAMIC are less than 0, so pOp->p4.p points to object on allocated on stack (in this particular case). Passing n > 0 we are implying that n bytes of memory starting from zP4 must be copied to new chunk, which comes with P4_DYNAMIC If you still have doubts, I assure you that I=E2=80=99ve already tried = to replace it and always get assertion faults like: Assertion failed: (f_type < field_type_MAX), function mem_apply_type, = file tarantool/src/box/sql/vdbe.c, line 334. In other places (where we use P4_DYNAMIC), field_type array is allocated on heap, so it can be freed after VDBE execution. As an option we can allocate on heap this string before calling sqlite3VdbeChangeP4, like this: diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index ca39faf51..27bbc1b1c 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -2860,11 +2860,15 @@ sqlite3CodeSubselect(Parse * pParse, /* = Parsing context */ r3 =3D = sqlite3ExprCodeTarget(pParse, pE2, r1); enum field_type type =3D = sql_affinity_to_field_type(affinity); - enum field_type types[2] =3D - { type, field_type_MAX = }; + size_t sz =3D 2 * sizeof(enum = field_type); + enum field_type *types =3D + = sqlite3DbMallocRaw(pParse->db, + sz); + types[0] =3D type; + types[1] =3D field_type_MAX; sqlite3VdbeAddOp4(v, = OP_MakeRecord, r3, 1, r2, (char = *)types, - = sizeof(types)); + P4_DYNAMIC); >>>> diff --git a/src/box/sql/where.c b/src/box/sql/where.c >>>> index 571b5af78..539296079 100644 >>>> --- a/src/box/sql/where.c >>>> +++ b/src/box/sql/where.c >>>> @@ -1200,7 +1200,7 @@ whereRangeSkipScanEst(Parse * pParse, = /* Parsing & code generating context */ >>>> int nLower =3D -1; >>>> int nUpper =3D index->def->opts.stat->sample_count + 1; >>>> int rc =3D SQLITE_OK; >>>> - u8 aff =3D sql_space_index_part_affinity(space->def, p, nEq); >>>> + u8 aff =3D p->key_def->parts[nEq].type; >>>=20 >>> 8. Why? Below in this function aff is used as affinity, not type. >> Am I missing smth? >> sqlite3Stat4ValueFromExpr -> stat4ValueFromExpr -> = sqlite3ValueApplyAffinity -> mem_apply_type >=20 > Naming confuses. All this functions except mem_apply_type take "u8 = affinity", > not "enum field_type type". Please, rename parameters and functions. As you wish. I just didn=E2=80=99t want to mix functional changes and non-functional refactoring since it complicates review process. diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h index 579f68fed..462cd233f 100644 --- a/src/box/sql/sqliteInt.h +++ b/src/box/sql/sqliteInt.h @@ -4375,8 +4375,9 @@ void sqlite3ValueSetStr(sqlite3_value *, int, = const void *, void sqlite3ValueSetNull(sqlite3_value *); void sqlite3ValueFree(sqlite3_value *); sqlite3_value *sqlite3ValueNew(sqlite3 *); -int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, sqlite3_value **); -void sqlite3ValueApplyAffinity(sqlite3_value *, u8); +int sqlite3ValueFromExpr(sqlite3 *, Expr *, enum field_type type, + sqlite3_value **); +void sqlite3ValueApplyAffinity(sqlite3_value *, enum field_type type); #ifndef SQLITE_AMALGAMATION extern const unsigned char sqlite3OpcodeProperty[]; extern const char sqlite3StrBINARY[]; @@ -4600,7 +4601,8 @@ int sqlite3ExprCheckIN(Parse *, Expr *); void sqlite3AnalyzeFunctions(void); int sqlite3Stat4ProbeSetValue(Parse *, struct index_def *, = UnpackedRecord **, Expr *, int, int, int *); -int sqlite3Stat4ValueFromExpr(Parse *, Expr *, u8, sqlite3_value **); +int sqlite3Stat4ValueFromExpr(Parse *, Expr *, enum field_type type, + sqlite3_value **); void sqlite3Stat4ProbeFree(UnpackedRecord *); =20 /** diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 61d73b676..32be5b9fa 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -394,9 +394,9 @@ int sqlite3_value_numeric_type(sqlite3_value *pVal) = { void sqlite3ValueApplyAffinity( sqlite3_value *pVal, - u8 affinity) + enum field_type type) { - mem_apply_type((Mem *) pVal, affinity); + mem_apply_type((Mem *) pVal, type); } =20 /* diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c index cd71641b0..7dc5dbfa9 100644 --- a/src/box/sql/vdbemem.c +++ b/src/box/sql/vdbemem.c @@ -1161,7 +1161,7 @@ valueNew(sqlite3 * db, struct ValueNewStat4Ctx *p) static int valueFromFunction(sqlite3 * db, /* The database connection */ Expr * p, /* The expression to evaluate */ - u8 aff, /* Affinity to use */ + enum field_type type, sqlite3_value ** ppVal, /* Write the new value = here */ struct ValueNewStat4Ctx *pCtx /* Second argument for = valueNew() */ ) @@ -1199,7 +1199,7 @@ valueFromFunction(sqlite3 * db, /* The database = connection */ } for (i =3D 0; i < nVal; i++) { rc =3D sqlite3ValueFromExpr(db, = pList->a[i].pExpr, - aff, &apVal[i]); + type, &apVal[i]); if (apVal[i] =3D=3D 0 || rc !=3D SQLITE_OK) goto value_from_function_out; } @@ -1220,7 +1220,7 @@ valueFromFunction(sqlite3 * db, /* The database = connection */ rc =3D ctx.isError; sqlite3ErrorMsg(pCtx->pParse, "%s", = sqlite3_value_text(pVal)); } else { - sqlite3ValueApplyAffinity(pVal, aff); + sqlite3ValueApplyAffinity(pVal, type); assert(rc =3D=3D SQLITE_OK); } pCtx->pParse->rc =3D rc; @@ -1253,7 +1253,7 @@ valueFromFunction(sqlite3 * db, /* The database = connection */ static int valueFromExpr(sqlite3 * db, /* The database connection */ Expr * pExpr, /* The expression to evaluate */ - u8 affinity, /* Affinity to use */ + enum field_type type, sqlite3_value ** ppVal, /* Write the new value here */ struct ValueNewStat4Ctx *pCtx /* Second argument for = valueNew() */ ) @@ -1284,7 +1284,7 @@ valueFromExpr(sqlite3 * db, /* The database = connection */ testcase(rc !=3D SQLITE_OK); if (*ppVal) { sqlite3VdbeMemCast(*ppVal, aff); - sqlite3ValueApplyAffinity(*ppVal, affinity); + sqlite3ValueApplyAffinity(*ppVal, type); } return rc; } @@ -1315,18 +1315,18 @@ valueFromExpr(sqlite3 * db, /* The database = connection */ goto no_mem; sqlite3ValueSetStr(pVal, -1, zVal, = SQLITE_DYNAMIC); } - if ((op =3D=3D TK_INTEGER || op =3D=3D TK_FLOAT) - && affinity =3D=3D AFFINITY_BLOB) { - sqlite3ValueApplyAffinity(pVal, AFFINITY_REAL); + if ((op =3D=3D TK_INTEGER || op =3D=3D TK_FLOAT) && + type =3D=3D FIELD_TYPE_SCALAR) { + sqlite3ValueApplyAffinity(pVal, = FIELD_TYPE_NUMBER); } else { - sqlite3ValueApplyAffinity(pVal, affinity); + sqlite3ValueApplyAffinity(pVal, type); } if (pVal->flags & (MEM_Int | MEM_Real)) pVal->flags &=3D ~MEM_Str; } else if (op =3D=3D TK_UMINUS) { /* This branch happens for multiple negative signs. Ex: = -(-5) */ if (SQLITE_OK =3D=3D - sqlite3ValueFromExpr(db, pExpr->pLeft, affinity, = &pVal) + sqlite3ValueFromExpr(db, pExpr->pLeft, type, &pVal) && pVal !=3D 0) { if ((rc =3D sqlite3VdbeMemNumerify(pVal)) !=3D = SQLITE_OK) return rc; @@ -1338,7 +1338,7 @@ valueFromExpr(sqlite3 * db, /* The database = connection */ } else { pVal->u.i =3D -pVal->u.i; } - sqlite3ValueApplyAffinity(pVal, affinity); + sqlite3ValueApplyAffinity(pVal, type); } } else if (op =3D=3D TK_NULL) { pVal =3D valueNew(db, pCtx); @@ -1364,7 +1364,7 @@ valueFromExpr(sqlite3 * db, /* The database = connection */ #endif =20 else if (op =3D=3D TK_FUNCTION && pCtx !=3D 0) { - rc =3D valueFromFunction(db, pExpr, affinity, &pVal, = pCtx); + rc =3D valueFromFunction(db, pExpr, type, &pVal, pCtx); } =20 *ppVal =3D pVal; @@ -1393,11 +1393,11 @@ valueFromExpr(sqlite3 * db, /* The database = connection */ int sqlite3ValueFromExpr(sqlite3 * db, /* The database connection */ Expr * pExpr, /* The expression to evaluate */ - u8 affinity, /* Affinity to use */ + enum field_type type, sqlite3_value ** ppVal /* Write the new value = here */ ) { - return pExpr ? valueFromExpr(db, pExpr, affinity, ppVal, 0) : 0; + return pExpr ? valueFromExpr(db, pExpr, type, ppVal, 0) : 0; } =20 /* @@ -1471,7 +1471,7 @@ sqlite3AnalyzeFunctions(void) static int stat4ValueFromExpr(Parse * pParse, /* Parse context */ Expr * pExpr, /* The expression to extract a = value from */ - u8 affinity, /* Affinity to use */ + enum field_type type, struct ValueNewStat4Ctx *pAlloc, /* How to = allocate space. Or NULL */ sqlite3_value ** ppVal /* OUT: New value object = (or NULL) */ ) @@ -1501,14 +1501,13 @@ stat4ValueFromExpr(Parse * pParse, /* Parse = context */ rc =3D sqlite3VdbeMemCopy((Mem *) pVal, = &v->aVar[iBindVar - 1]); if (rc =3D=3D SQLITE_OK) { - sqlite3ValueApplyAffinity(pVal, - = affinity); + sqlite3ValueApplyAffinity(pVal, = type); } pVal->db =3D pParse->db; } } } else { - rc =3D valueFromExpr(db, pExpr, affinity, &pVal, = pAlloc); + rc =3D valueFromExpr(db, pExpr, type, &pVal, pAlloc); } =20 assert(pVal =3D=3D 0 || pVal->db =3D=3D db); @@ -1606,11 +1605,11 @@ sqlite3Stat4ProbeSetValue(Parse * pParse, = /* Parse context */ int sqlite3Stat4ValueFromExpr(Parse * pParse, /* Parse context */ Expr * pExpr, /* The expression to extract a = value from */ - u8 affinity, /* Affinity to use */ + enum field_type type, sqlite3_value ** ppVal /* OUT: New = value object (or NULL) */ ) { - return stat4ValueFromExpr(pParse, pExpr, affinity, 0, ppVal); + return stat4ValueFromExpr(pParse, pExpr, type, 0, ppVal); >> diff --git a/src/box/sql/build.c b/src/box/sql/build.c >> index e51e2db2a..514d0ca9d 100644 >> --- a/src/box/sql/build.c >> +++ b/src/box/sql/build.c> diff --git a/src/box/sql/delete.c = b/src/box/sql/delete.c >> index f9c42fdec..ca6c49373 100644 >> --- a/src/box/sql/delete.c >> +++ b/src/box/sql/delete.c >> @@ -592,13 +592,13 @@ sql_generate_index_key(struct Parse *parse, = struct index *index, int cursor, >> * is an integer, then it might be stored in the >> * table as an integer (using a compact >> * representation) then converted to REAL by an >> - * OP_RealAffinity opcode. But we are getting >> + * OP_Realify opcode. But we are getting >=20 > 1. OP_RealAffinity still is mentioned in sqliteInt.h in > a comment. Removed: diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h index 579f68fed..cbdd1f1a3 100644 --- a/src/box/sql/sqliteInt.h +++ b/src/box/sql/sqliteInt.h @@ -4459,7 +4460,7 @@ int sqlite3ResolveOrderGroupBy(Parse *, Select *, = ExprList *, const char *); * function is capable of transforming these types of expressions into * sqlite3_value objects. * - * If parameter iReg is not negative, code an OP_RealAffinity = instruction + * If parameter iReg is not negative, code an OP_Realify instruction * on register iReg. This is used when an equivalent integer value is * stored in place of an 8-byte floating point value in order to save * space. >> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c >> index 0a80ca622..ca39faf51 100644 >> --- a/src/box/sql/expr.c >> +++ b/src/box/sql/expr.c >> @@ -3172,7 +3177,10 @@ sqlite3ExprCodeIN(Parse * pParse, /* = Parsing and code generating context */ >> * of the RHS using the LHS as a probe. If found, the result = is >> * true. >> */ >> - sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, = nVector); >> + enum field_type *types =3D = sql_affinity_str_to_field_type_str(zAff); >> + types[nVector] =3D field_type_MAX; >> + sqlite3VdbeAddOp4(v, OP_ApplyType, rLhs, nVector, 0, (char = *)types, >> + P4_DYNAMIC); >=20 > 2. Why do you need types[nVector] =3D field_type_MAX? As I see, before = your patch > there was no additional zero termination. To cut off array of types. Before my patch, first nVector bytes were copied inside vdbeChangeP4Full() and automatically terminated with NULL (sqlite3DbStrNDup). >> sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector); Now it makes no sense to copy this string: we can set array termination and pass it with dynamic flag. >> --- a/src/box/sql/update.c >> +++ b/src/box/sql/update.c >> @@ -274,11 +274,12 @@ sqlite3Update(Parse * pParse, /* = The parser context */ >> nKey =3D pk_part_count; >> regKey =3D iPk; >> } else { >> - const char *zAff =3D is_view ? 0 : >> - = sql_space_index_affinity_str(pParse->db, def, >> - = pPk->def); >> + enum field_type *types =3D is_view ? NULL : >> + = sql_index_type_str(pParse->db, >> + = pPk->def); >> sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, = pk_part_count, >> - regKey, zAff, pk_part_count); >> + regKey, (char *) types, >> + is_view ? 0 : P4_DYNAMIC); >=20 > 3. I guess, here and in other places, where type str is either 0 > or not 0, you can always set P4_DYNAMIC, it looks a bit simpler > and works as well. "is_view ? 0 : P4_DYNAMIC" -> "P4_DYNAMIC=E2=80=9D. Ok, indeed it is valid replacement: diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c index ca6c49373..a6e7547f5 100644 --- a/src/box/sql/delete.c +++ b/src/box/sql/delete.c @@ -336,8 +336,7 @@ sql_table_delete_from(struct Parse *parse, struct = SrcList *tab_list, = sql_index_type_str(parse->db, = pk->def); sqlite3VdbeAddOp4(v, OP_MakeRecord, reg_pk, = pk_len, - reg_key, (char *)types, = is_view ? 0 : - = P4_DYNAMIC); + reg_key, (char *)types, = P4_DYNAMIC); /* Set flag to save memory allocating one * by malloc. diff --git a/src/box/sql/update.c b/src/box/sql/update.c index fd74817ea..cea840a99 100644 --- a/src/box/sql/update.c +++ b/src/box/sql/update.c @@ -278,8 +278,7 @@ sqlite3Update(Parse * pParse, /* The = parser context */ sql_index_type_str(pParse->db, pPk->def); sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count, - regKey, (char *) types, - is_view ? 0 : P4_DYNAMIC); + regKey, (char *) types, P4_DYNAMIC); /* * Set flag to save memory allocating one by * malloc. >> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c >> index 4345af24e..61d73b676 100644 >> --- a/src/box/sql/vdbe.c >> +++ b/src/box/sql/vdbe.c >> @@ -306,32 +306,35 @@ applyNumericAffinity(Mem *pRec, int bTryForInt) >> } >> /** >> - * Processing is determine by the affinity parameter: >> + * Processing is determine by the field type parameter: >=20 > 4. Not sure, if determine is an adjective or a noun. It is > always a verb. So it should be 'is determine*D*', shouldn't it? Yep, it is my (or not my) mistake. Fixed: diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 61d73b676..a38075e2e 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -306,7 +306,7 @@ applyNumericAffinity(Mem *pRec, int bTryForInt) } =20 /** - * Processing is determine by the field type parameter: + * Processing is determined by the field type parameter: * >> * >> - * AFFINITY_INTEGER: >> - * AFFINITY_REAL: >> - * Try to convert mem to an integer representation or a >> - * floating-point representation if an integer representation >> - * is not possible. Note that the integer representation is >> - * always preferred, even if the affinity is REAL, because >> - * an integer representation is more space efficient on disk. >> + * 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. >> * >> - * AFFINITY_TEXT: >> - * Convert mem to a text representation. >> + * NUMBER: >> + * If memory holds INT or floating point value, >> + * no actions take place. >> * >> - * AFFINITY_BLOB: >> - * No-op. mem is unchanged. >> + * STRING: >> + * Convert mem to a string representation. >> * >> - * @param record The value to apply affinity to. >> - * @param affinity The affinity to be applied. >> + * SCALAR: >> + * Mem is unchanged, but flag is set to BLOB. >> + * >> + * @param record The value to apply type to. >> + * @param type_t The type to be applied. >=20 > 5. type_t -> f_type. By the way, why f_type and not just type? Hmm, I was sure that I got compile error since other variable already occupied this name=E2=80=A6 Nevermind, lets use simple =E2=80=99type=E2=80=99 name: diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 61d73b676..17921ce36 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -324,15 +324,15 @@ applyNumericAffinity(Mem *pRec, int bTryForInt) * Mem is unchanged, but flag is set to BLOB. * * @param record The value to apply type to. - * @param type_t The type to be applied. + * @param type The type to be applied. */ static int -mem_apply_type(struct Mem *record, enum field_type f_type) +mem_apply_type(struct Mem *record, enum field_type type) { if ((record->flags & MEM_Null) !=3D 0) return 0; - assert(f_type < field_type_MAX); - switch (f_type) { + assert(type < field_type_MAX); + switch (type) { case FIELD_TYPE_INTEGER: case FIELD_TYPE_UNSIGNED: if ((record->flags & MEM_Int) =3D=3D MEM_Int) >> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c >> index b124a1d53..efbc91cf8 100644 >> --- a/src/box/sql/wherecode.c >> +++ b/src/box/sql/wherecode.c >> @@ -396,9 +396,12 @@ codeApplyAffinity(Parse * pParse, int base, int = n, char *zAff) >> n--; >> } >> - /* Code the OP_Affinity opcode if there is anything left to = do. */ >> if (n > 0) { >> - sqlite3VdbeAddOp4(v, OP_Affinity, base, n, 0, zAff, = n); >> + enum field_type *types =3D >> + sql_affinity_str_to_field_type_str(zAff); >> + types[n] =3D field_type_MAX; >=20 > 6. The same as 2. See answer above: here we need only first n fields, not the whole array.