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 10FDA6EC56; Wed, 28 Jul 2021 23:52:39 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 10FDA6EC56 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1627505559; bh=fIEmZIIakJSzIxOfFLhQI1zEyhk8ZiLqsb/IBlywwLo=; 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=xYVIE5rf7EZIgo6xHqammqAA3u6njb2NSuiqPKr1qbMThOxSpAj8pRCzgxq62IKNS QTTYt58X0nAKVMJj304pCFD5xfEjJK3pfjjrQHNHtqUsUbmQdHju0hoZ+TBKCk/Se5 Vf9eIxn8Nj5ikc9g6EVCEQEd7faNOZJUZOfhk8rs= 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 dev.tarantool.org (Postfix) with ESMTPS id E1ADF6EC5E for ; Wed, 28 Jul 2021 23:51:13 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org E1ADF6EC5E Received: by smtp58.i.mail.ru with esmtpa (envelope-from ) id 1m8qWG-0007RY-KN; Wed, 28 Jul 2021 23:51:13 +0300 To: v.shpilevoy@tarantool.org Cc: tarantool-patches@dev.tarantool.org Date: Wed, 28 Jul 2021 23:51:12 +0300 Message-Id: <8722fbfce0455e13c01672793f6a5075dc0fc458.1627504973.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-4EC0790: 10 X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD941C43E597735A9C351B198F4576AC7B2770D7874BA03B4AE182A05F5380850407767C53364D7A460A408A1E845F749FBCCB265630531697BA48670D22D55D4C9 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7E22BCA80EDA7B036EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637205505A8D8EF484BEA1F7E6F0F101C6723150C8DA25C47586E58E00D9D99D84E1BDDB23E98D2D38BBCA57AF85F7723F2B54E3E9CD0AB610F1B77BF0A92EBCB15CC7F00164DA146DAFE8445B8C89999728AA50765F7900637F6B57BC7E64490618DEB871D839B7333395957E7521B51C2DFABB839C843B9C08941B15DA834481F8AA50765F7900637F6B57BC7E6449061A352F6E88A58FB86F5D81C698A659EA73AA81AA40904B5D9A18204E546F3947CD7F4798FD4FA8F52AD7EC71F1DB884274AD6D5ED66289B52698AB9A7B718F8C46E0066C2D8992A16725E5C173C3A84C3BE84CE7CBC23D9FEBA3038C0950A5D36B5C8C57E37DE458B0BC6067A898B09E46D1867E19FE14079C09775C1D3CA48CF3D321E7403792E342EB15956EA79C166A417C69337E82CC275ECD9A6C639B01B78DA827A17800CE75A9E79F66F1C28F3731C566533BA786AA5CC5B56E945C8DA X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A213B5FB47DCBC3458F0AFF96BAACF4158235E5A14AD4A4A4625E192CAD1D9E79DB53CE843736870890A61D6163D015090 X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975C7BEA09003D200E087A15062584C602EBCB10E468D1BCCBA29C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EF795D7D556640A06E699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D342B1F2AD168155B06F1B680840331442694003E6FDD7A599513F3A8B9B6FD0FB320C8FEAB35C02F5C1D7E09C32AA3244C6AA5CA73CD3FE0059E9D893B7C8707A169B6CAE0477E908D729B2BEF169E0186 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojbL9S8ysBdXi4QUX63t/5ttHDQpEmzoMw X-Mailru-Sender: 3A338A78718AEC5ABE350BD77BF82E15D66EC3EED26C43C7B8AE9086F6C92050EAFFB435BF9D44E0A3E7B4BFDCAD2EFE027D9DD7AE851095A2E8D17B49942DB0CBEE3F9BE14373499437F6177E88F7363CDA0F3B3F5B9367 X-Mras: Ok Subject: [Tarantool-patches] [PATCH v1 3/7] sql: rework OP_Seek* opcodes 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 changes the Seek* opcodes that are used to search in space using index. After the redesign, searches using these opcodes work according to the new implicit casting rules. However, currently implicit cast in these opcodes is not invoked since there is OP_ApplyType before them. Unnecessary OP_ApplyType calls will be removed in next patch. Part of 4230 Part of 4470 --- src/box/sql.c | 69 +----- src/box/sql/cursor.c | 14 -- src/box/sql/cursor.h | 1 - src/box/sql/mem.c | 2 - src/box/sql/mem.h | 17 ++ src/box/sql/tarantoolInt.h | 3 + src/box/sql/vdbe.c | 495 ++++++++++++++++++++----------------- 7 files changed, 299 insertions(+), 302 deletions(-) diff --git a/src/box/sql.c b/src/box/sql.c index 433264abe..a6a7864f1 100644 --- a/src/box/sql.c +++ b/src/box/sql.c @@ -204,75 +204,20 @@ int tarantoolsqlPrevious(BtCursor *pCur, int *pRes) return cursor_advance(pCur, pRes); } -int tarantoolsqlMovetoUnpacked(BtCursor *pCur, UnpackedRecord *pIdxKey, - int *pRes) +int +sql_cursor_seek(struct BtCursor *cur, struct Mem *mems, uint32_t len, int *res) { struct region *region = &fiber()->gc; size_t used = region_used(region); - uint32_t tuple_size; - const char *tuple = - sql_vdbe_mem_encode_tuple(pIdxKey->aMem, pIdxKey->nField, - &tuple_size, region); + uint32_t size; + const char *tuple = sql_vdbe_mem_encode_tuple(mems, len, &size, region); if (tuple == NULL) return -1; - if (key_alloc(pCur, tuple_size) != 0) + if (key_alloc(cur, size) != 0) return -1; - memcpy(pCur->key, tuple, tuple_size); + memcpy(cur->key, tuple, size); region_truncate(region, used); - - int rc, res_success; - switch (pIdxKey->opcode) { - default: - /* "Unexpected opcode" */ - assert(0); - case 255: - /* Restore saved state. Just re-seek cursor. - TODO: replace w/ named constant. */ - res_success = 0; - break; - case OP_SeekLT: - pCur->iter_type = ITER_LT; - res_success = -1; /* itemiter_type = (pCur->hints & OPFLAG_SEEKEQ) != 0 ? - ITER_REQ : ITER_LE; - res_success = 0; /* item==key */ - break; - case OP_SeekGE: - pCur->iter_type = (pCur->hints & OPFLAG_SEEKEQ) != 0 ? - ITER_EQ : ITER_GE; - res_success = 0; /* item==key */ - break; - case OP_SeekGT: - pCur->iter_type = ITER_GT; - res_success = 1; /* item>key */ - break; - case OP_NoConflict: - case OP_NotFound: - case OP_Found: - case OP_IdxDelete: - pCur->iter_type = ITER_EQ; - res_success = 0; - break; - } - rc = cursor_seek(pCur, pRes); - if (*pRes == 0) { - *pRes = res_success; - /* - * To select the first item in a row of equal items - * (last item), sql comparator is configured to - * return +1 (-1) if an item equals the key making it - * impossible to distinguish from an item>key (itemeqSeen = 1; - } else { - *pRes = -1; /* -1 also means EOF */ - } - return rc; + return cursor_seek(cur, res); } /* diff --git a/src/box/sql/cursor.c b/src/box/sql/cursor.c index bb2dae898..694fd9a7f 100644 --- a/src/box/sql/cursor.c +++ b/src/box/sql/cursor.c @@ -132,20 +132,6 @@ sqlCursorPayload(BtCursor *pCur, u32 offset, u32 amt, void *pBuf) * is larger than pIdxKey. */ -int -sqlCursorMovetoUnpacked(BtCursor * pCur, /* The cursor to be moved */ - UnpackedRecord * pIdxKey, /* Unpacked index key */ - int *pRes /* Write search results here */ - ) -{ - assert(pRes); - assert(pIdxKey); - assert((pCur->curFlags & BTCF_TaCursor) || - (pCur->curFlags & BTCF_TEphemCursor)); - - return tarantoolsqlMovetoUnpacked(pCur, pIdxKey, pRes); -} - int sqlCursorNext(BtCursor *pCur, int *pRes) { diff --git a/src/box/sql/cursor.h b/src/box/sql/cursor.h index 88e544191..b82d69e9c 100644 --- a/src/box/sql/cursor.h +++ b/src/box/sql/cursor.h @@ -60,7 +60,6 @@ void sqlCursorZero(BtCursor *); */ void sql_cursor_close(struct BtCursor *cursor); -int sqlCursorMovetoUnpacked(BtCursor *, UnpackedRecord * pUnKey, int *pRes); int sqlCursorNext(BtCursor *, int *pRes); int sqlCursorPrevious(BtCursor *, int *pRes); diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c index b04303be2..98b367054 100644 --- a/src/box/sql/mem.c +++ b/src/box/sql/mem.c @@ -2698,8 +2698,6 @@ sqlVdbeRecordCompareMsgpack(const void *key1, return -rc; } } - - key2->eqSeen = 1; return key2->default_rc; } diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h index 47a940c56..5d1d7592e 100644 --- a/src/box/sql/mem.h +++ b/src/box/sql/mem.h @@ -1028,3 +1028,20 @@ mpstream_encode_vdbe_mem(struct mpstream *stream, struct Mem *var); char * sql_vdbe_mem_encode_tuple(struct Mem *fields, uint32_t field_count, uint32_t *tuple_size, struct region *region); + +static inline bool +is_mem_num_min(const struct Mem *mem) +{ + return (mem->field_type == FIELD_TYPE_INTEGER && + mem->type == MEM_TYPE_INT && mem->u.i == INT64_MIN) || + (mem->field_type == FIELD_TYPE_UNSIGNED && + mem->type == MEM_TYPE_UINT && mem->u.u == 0); +} + +static inline bool +is_mem_num_max(const struct Mem *mem) +{ + return (mem->field_type == FIELD_TYPE_INTEGER || + mem->field_type == FIELD_TYPE_UNSIGNED) && + mem->type == MEM_TYPE_UINT && mem->u.u == 0; +} diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h index 1ded6c709..8fdc50432 100644 --- a/src/box/sql/tarantoolInt.h +++ b/src/box/sql/tarantoolInt.h @@ -27,6 +27,9 @@ int tarantoolsqlReplace(struct space *space, const char *tuple, const char *tuple_end); int tarantoolsqlDelete(BtCursor * pCur, u8 flags); +int +sql_cursor_seek(struct BtCursor *cur, struct Mem *mems, uint32_t len, int *res); + /** * Delete entry from space by its key. * diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 62f58def9..a69402720 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -2506,57 +2506,6 @@ case OP_Close: { break; } -/* Opcode: SeekGE P1 P2 P3 P4 P5 - * Synopsis: key=r[P3@P4] - * - * If cursor P1 refers to an SQL table (B-Tree that uses integer keys), - * use the value in register P3 as the key. If cursor P1 refers - * to an SQL index, then P3 is the first in an array of P4 registers - * that are used as an unpacked index key. - * - * Reposition cursor P1 so that it points to the smallest entry that - * is greater than or equal to the key value. If there are no records - * greater than or equal to the key and P2 is not zero, then jump to P2. - * - * If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this - * opcode will always land on a record that equally equals the key, or - * else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this - * opcode must be followed by an IdxLE opcode with the same arguments. - * The IdxLE opcode will be skipped if this opcode succeeds, but the - * IdxLE opcode will be used on subsequent loop iterations. - * - * This opcode leaves the cursor configured to move in forward order, - * from the beginning toward the end. In other words, the cursor is - * configured to use Next, not Prev. - * - * If P5 is not zero, than it is offset of integer fields in input - * vector. Force corresponding value to be INTEGER, in case it - * is floating point value. Alongside with that, type of - * iterator may be changed: a > 1.5 -> a >= 2. - * - * See also: Found, NotFound, SeekLt, SeekGt, SeekLe - */ -/* Opcode: SeekGT P1 P2 P3 P4 P5 - * Synopsis: key=r[P3@P4] - * - * If cursor P1 refers to an SQL table (B-Tree that uses integer keys), - * use the value in register P3 as a key. If cursor P1 refers - * to an SQL index, then P3 is the first in an array of P4 registers - * that are used as an unpacked index key. - * - * Reposition cursor P1 so that it points to the smallest entry that - * is greater than the key value. If there are no records greater than - * the key and P2 is not zero, then jump to P2. - * - * This opcode leaves the cursor configured to move in forward order, - * from the beginning toward the end. In other words, the cursor is - * configured to use Next, not Prev. - * - * If P5 is not zero, than it is offset of integer fields in input - * vector. Force corresponding value to be INTEGER. - * - * P5 has the same meaning as for SeekGE. - */ /* Opcode: SeekLT P1 P2 P3 P4 P5 * Synopsis: key=r[P3@P4] * @@ -2577,6 +2526,62 @@ case OP_Close: { * * See also: Found, NotFound, SeekGt, SeekGe, SeekLe */ +case OP_SeekLT: { /* jump, in3 */ + struct VdbeCursor *cur = p->apCsr[pOp->p1]; +#ifdef SQL_DEBUG + cur->seekOp = pOp->opcode; +#endif + cur->nullRow = 0; + cur->uc.pCursor->iter_type = ITER_LT; + + uint32_t len = pOp->p4.i; + assert(pOp->p4type == P4_INT32); + assert(len <= cur->key_def->part_count); + struct Mem *mems = &aMem[pOp->p3]; + bool is_le = false; + bool is_zero = false; + for (uint32_t i = 0; i < len; ++i) { + enum field_type type = cur->key_def->parts[i].type; + struct Mem *mem = &mems[i]; + if (mem_is_field_compatible(mem, type)) + continue; + if (!sql_type_is_numeric(type) || !mem_is_num(mem)) { + diag_set(ClientError, ER_SQL_TYPE_MISMATCH, + mem_str(mem), field_type_strs[type]); + goto abort_due_to_error; + } + int cmp = mem_cast_implicit_number(mem, type); + is_le = is_le || cmp > 0; + /* + * If number before cast were less than min possible for given + * field type, than there is no point to use iterator since we + * won't find anything. + */ + is_zero = is_zero || (is_mem_num_min(mem) && cmp < 0); + } + if (is_zero) { + assert(pOp->p2 > 0); + VdbeBranchTaken(1, 2); + goto jump_to_p2; + } + if (is_le) + cur->uc.pCursor->iter_type = ITER_LE; + + int res; + if (sql_cursor_seek(cur->uc.pCursor, mems, len, &res) != 0) + goto abort_due_to_error; + assert((res != 0) == (cur->uc.pCursor->eState == CURSOR_INVALID)); + cur->cacheStatus = CACHE_STALE; +#ifdef SQL_TEST + sql_search_count++; +#endif + assert(pOp->p2 > 0); + VdbeBranchTaken(res, 2); + if (res != 0) + goto jump_to_p2; + break; +} + /* Opcode: SeekLE P1 P2 P3 P4 P5 * Synopsis: key=r[P3@P4] * @@ -2604,188 +2609,236 @@ case OP_Close: { * * See also: Found, NotFound, SeekGt, SeekGe, SeekLt */ -case OP_SeekLT: /* jump, in3 */ -case OP_SeekLE: /* jump, in3 */ -case OP_SeekGE: /* jump, in3 */ -case OP_SeekGT: { /* jump, in3 */ - int res; /* Comparison result */ - int oc; /* Opcode */ - VdbeCursor *pC; /* The cursor to seek */ - UnpackedRecord r; /* The key to seek for */ - int nField; /* Number of columns or fields in the key */ - i64 iKey; /* The id we are to seek to */ - int eqOnly; /* Only interested in == results */ - - assert(pOp->p1>=0 && pOp->p1nCursor); - assert(pOp->p2!=0); - pC = p->apCsr[pOp->p1]; - assert(pC!=0); - assert(pC->eCurType==CURTYPE_TARANTOOL); - assert(OP_SeekLE == OP_SeekLT+1); - assert(OP_SeekGE == OP_SeekLT+2); - assert(OP_SeekGT == OP_SeekLT+3); - assert(pC->uc.pCursor!=0); - oc = pOp->opcode; - eqOnly = 0; - pC->nullRow = 0; +case OP_SeekLE: { /* jump, in3 */ + struct VdbeCursor *cur = p->apCsr[pOp->p1]; #ifdef SQL_DEBUG - pC->seekOp = pOp->opcode; + cur->seekOp = pOp->opcode; #endif - iKey = 0; - /* - * In case floating value is intended to be passed to - * iterator over integer field, we must truncate it to - * integer value and change type of iterator: - * a > 1.5 -> a >= 2 - */ - int int_field = pOp->p5; - bool is_neg = false; - - if (int_field > 0) { - /* The input value in P3 might be of any type: integer, real, string, - * blob, or NULL. But it needs to be an integer before we can do - * the seek, so convert it. - */ - pIn3 = &aMem[int_field]; - if (mem_is_null(pIn3)) - goto skip_truncate; - if (mem_is_str(pIn3)) - mem_to_number(pIn3); - int64_t i; - if (mem_get_int(pIn3, &i, &is_neg) != 0) { - if (!mem_is_double(pIn3)) { - diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - mem_str(pIn3), "integer"); - goto abort_due_to_error; - } - double d = pIn3->u.r; - assert(d >= (double)INT64_MAX || d < (double)INT64_MIN); - /* TODO: add [INT64_MAX, UINT64_MAX) here. */ - if (d > (double)INT64_MAX) - i = INT64_MAX; - else if (d < (double)INT64_MIN) - i = INT64_MIN; - else - i = d; - is_neg = i < 0; + cur->nullRow = 0; + bool is_eq = (cur->uc.pCursor->hints & OPFLAG_SEEKEQ) != 0; + cur->uc.pCursor->iter_type = is_eq ? ITER_REQ : ITER_LE; + assert(!is_eq || pOp[1].opcode == OP_IdxLT); + + uint32_t len = pOp->p4.i; + assert(pOp->p4type == P4_INT32); + assert(len <= cur->key_def->part_count); + struct Mem *mems = &aMem[pOp->p3]; + bool is_lt = false; + bool is_zero = false; + for (uint32_t i = 0; i < len; ++i) { + enum field_type type = cur->key_def->parts[i].type; + struct Mem *mem = &mems[i]; + if (mem_is_field_compatible(mem, type)) + continue; + if (!sql_type_is_numeric(type) || !mem_is_num(mem)) { + diag_set(ClientError, ER_SQL_TYPE_MISMATCH, + mem_str(mem), field_type_strs[type]); + goto abort_due_to_error; } - iKey = i; - - /* If the P3 value could not be converted into an integer without - * loss of information, then special processing is required... + int cmp = mem_cast_implicit_number(mem, type); + is_lt = is_lt || cmp < 0; + /* + * If number before cast were less than min possible for given + * field type, than there is no point to use iterator since we + * won't find anything. Also, in case search using EQ, we will + * not find anything if conversion cannot be precise. */ - if (!mem_is_int(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 - */ - VdbeBranchTaken(1,2); goto jump_to_p2; - break; - } - - /* If the approximation iKey is larger than the actual real search - * term, substitute >= for > and < for <=. e.g. if the search term - * is 4.9 and the integer approximation 5: - * - * (x > 4.9) -> (x >= 5) - * (x <= 4.9) -> (x < 5) - */ - if (pIn3->u.r<(double)iKey) { - assert(OP_SeekGE==(OP_SeekGT-1)); - assert(OP_SeekLT==(OP_SeekLE-1)); - assert((OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001)); - if ((oc & 0x0001)==(OP_SeekGT & 0x0001)) oc--; - } - - /* If the approximation iKey is smaller than the actual real search - * term, substitute <= for < and > for >=. - */ - else if (pIn3->u.r>(double)iKey) { - assert(OP_SeekLE==(OP_SeekLT+1)); - assert(OP_SeekGT==(OP_SeekGE+1)); - assert((OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001)); - if ((oc & 0x0001)==(OP_SeekLT & 0x0001)) oc++; - } - } + is_zero = is_zero || (is_eq && cmp != 0) || + (is_mem_num_min(mem) && cmp < 0); } -skip_truncate: - /* - * For a cursor with the OPFLAG_SEEKEQ hint, only the - * OP_SeekGE and OP_SeekLE opcodes are allowed, and these - * must be immediately followed by an OP_IdxGT or - * OP_IdxLT opcode, respectively, with the same key. - */ - if ((pC->uc.pCursor->hints & OPFLAG_SEEKEQ) != 0) { - eqOnly = 1; - assert(pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE); - assert(pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT); - assert(pOp[1].p1==pOp[0].p1); - assert(pOp[1].p2==pOp[0].p2); - assert(pOp[1].p3==pOp[0].p3); - assert(pOp[1].p4.i==pOp[0].p4.i); + if (is_zero) { + assert(pOp->p2 > 0); + VdbeBranchTaken(1, 2); + goto jump_to_p2; } + if (!is_eq && is_lt) + cur->uc.pCursor->iter_type = ITER_LT; - nField = pOp->p4.i; - assert(pOp->p4type==P4_INT32); - assert(nField>0); - r.key_def = pC->key_def; - r.nField = (u16)nField; - - if (int_field > 0) - mem_set_int(&aMem[int_field], iKey, is_neg); - - r.default_rc = ((1 & (oc - OP_SeekLT)) ? -1 : +1); - assert(oc!=OP_SeekGT || r.default_rc==-1); - assert(oc!=OP_SeekLE || r.default_rc==-1); - assert(oc!=OP_SeekGE || r.default_rc==+1); - assert(oc!=OP_SeekLT || r.default_rc==+1); + int res; + if (sql_cursor_seek(cur->uc.pCursor, mems, len, &res) != 0) + goto abort_due_to_error; + assert((res != 0) == (cur->uc.pCursor->eState == CURSOR_INVALID)); + cur->cacheStatus = CACHE_STALE; +#ifdef SQL_TEST + sql_search_count++; +#endif + assert(pOp->p2 > 0); + VdbeBranchTaken(res, 2); + if (res != 0) + goto jump_to_p2; + /* Skip the OP_IdxLT that follows if we have EQ. */ + if (is_eq) + pOp++; + break; +} - r.aMem = &aMem[pOp->p3]; +/* Opcode: SeekGE P1 P2 P3 P4 P5 + * Synopsis: key=r[P3@P4] + * + * If cursor P1 refers to an SQL table (B-Tree that uses integer keys), + * use the value in register P3 as the key. If cursor P1 refers + * to an SQL index, then P3 is the first in an array of P4 registers + * that are used as an unpacked index key. + * + * Reposition cursor P1 so that it points to the smallest entry that + * is greater than or equal to the key value. If there are no records + * greater than or equal to the key and P2 is not zero, then jump to P2. + * + * If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this + * opcode will always land on a record that equally equals the key, or + * else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this + * opcode must be followed by an IdxLE opcode with the same arguments. + * The IdxLE opcode will be skipped if this opcode succeeds, but the + * IdxLE opcode will be used on subsequent loop iterations. + * + * This opcode leaves the cursor configured to move in forward order, + * from the beginning toward the end. In other words, the cursor is + * configured to use Next, not Prev. + * + * If P5 is not zero, than it is offset of integer fields in input + * vector. Force corresponding value to be INTEGER, in case it + * is floating point value. Alongside with that, type of + * iterator may be changed: a > 1.5 -> a >= 2. + * + * See also: Found, NotFound, SeekLt, SeekGt, SeekLe + */ +case OP_SeekGE: { /* jump, in3 */ + struct VdbeCursor *cur = p->apCsr[pOp->p1]; #ifdef SQL_DEBUG - { int i; for(i=0; iseekOp = pOp->opcode; #endif - r.eqSeen = 0; - r.opcode = oc; - if (sqlCursorMovetoUnpacked(pC->uc.pCursor, &r, &res) != 0) - goto abort_due_to_error; - if (eqOnly && r.eqSeen==0) { - assert(res!=0); - goto seek_not_found; + cur->nullRow = 0; + bool is_eq = (cur->uc.pCursor->hints & OPFLAG_SEEKEQ) != 0; + cur->uc.pCursor->iter_type = is_eq ? ITER_EQ : ITER_GE; + assert(!is_eq || pOp[1].opcode == OP_IdxGT); + + uint32_t len = pOp->p4.i; + assert(pOp->p4type == P4_INT32); + assert(len <= cur->key_def->part_count); + struct Mem *mems = &aMem[pOp->p3]; + bool is_gt = false; + bool is_zero = false; + for (uint32_t i = 0; i < len; ++i) { + enum field_type type = cur->key_def->parts[i].type; + struct Mem *mem = &mems[i]; + if (mem_is_field_compatible(mem, type)) + continue; + if (!sql_type_is_numeric(type) || !mem_is_num(mem)) { + diag_set(ClientError, ER_SQL_TYPE_MISMATCH, + mem_str(mem), field_type_strs[type]); + goto abort_due_to_error; + } + int cmp = mem_cast_implicit_number(mem, type); + is_gt = is_gt || cmp > 0; + /* + * If number before cast were more than max possible for given + * field type, than there is no point to use iterator since we + * won't find anything. Also, in case search using EQ, we will + * not find anything if conversion cannot be precise. + */ + is_zero = is_zero || (is_eq && cmp != 0) || + (is_mem_num_max(mem) && cmp > 0); } - pC->cacheStatus = CACHE_STALE; + if (is_zero) { + assert(pOp->p2 > 0); + VdbeBranchTaken(1, 2); + goto jump_to_p2; + } + if (!is_eq && is_gt) + cur->uc.pCursor->iter_type = ITER_GT; + + int res; + if (sql_cursor_seek(cur->uc.pCursor, mems, len, &res) != 0) + goto abort_due_to_error; + assert((res != 0) == (cur->uc.pCursor->eState == CURSOR_INVALID)); + cur->cacheStatus = CACHE_STALE; #ifdef SQL_TEST sql_search_count++; #endif - if (oc>=OP_SeekGE) { assert(oc==OP_SeekGE || oc==OP_SeekGT); - if (res<0 || (res==0 && oc==OP_SeekGT)) { - res = 0; - if (sqlCursorNext(pC->uc.pCursor, &res) != 0) - goto abort_due_to_error; - } else { - res = 0; - } - } else { - assert(oc==OP_SeekLT || oc==OP_SeekLE); - if (res>0 || (res==0 && oc==OP_SeekLT)) { - res = 0; - if (sqlCursorPrevious(pC->uc.pCursor, &res) != 0) - goto abort_due_to_error; - } else { - /* res might be negative because the table is empty. Check to - * see if this is the case. - */ - res = (CURSOR_VALID != pC->uc.pCursor->eState); + assert(pOp->p2 > 0); + VdbeBranchTaken(res, 2); + if (res != 0) + goto jump_to_p2; + /* Skip the OP_IdxGT that follows if we have EQ. */ + if (is_eq) + pOp++; + break; +} + +/* Opcode: SeekGT P1 P2 P3 P4 P5 + * Synopsis: key=r[P3@P4] + * + * If cursor P1 refers to an SQL table (B-Tree that uses integer keys), + * use the value in register P3 as a key. If cursor P1 refers + * to an SQL index, then P3 is the first in an array of P4 registers + * that are used as an unpacked index key. + * + * Reposition cursor P1 so that it points to the smallest entry that + * is greater than the key value. If there are no records greater than + * the key and P2 is not zero, then jump to P2. + * + * This opcode leaves the cursor configured to move in forward order, + * from the beginning toward the end. In other words, the cursor is + * configured to use Next, not Prev. + * + * If P5 is not zero, than it is offset of integer fields in input + * vector. Force corresponding value to be INTEGER. + * + * P5 has the same meaning as for SeekGE. + */ +case OP_SeekGT: { /* jump, in3 */ + struct VdbeCursor *cur = p->apCsr[pOp->p1]; +#ifdef SQL_DEBUG + cur->seekOp = pOp->opcode; +#endif + cur->nullRow = 0; + cur->uc.pCursor->iter_type = ITER_GT; + + uint32_t len = pOp->p4.i; + assert(pOp->p4type == P4_INT32); + assert(len <= cur->key_def->part_count); + struct Mem *mems = &aMem[pOp->p3]; + bool is_ge = false; + bool is_zero = false; + for (uint32_t i = 0; i < len; ++i) { + enum field_type type = cur->key_def->parts[i].type; + struct Mem *mem = &mems[i]; + if (mem_is_field_compatible(mem, type)) + continue; + if (!sql_type_is_numeric(type) || !mem_is_num(mem)) { + diag_set(ClientError, ER_SQL_TYPE_MISMATCH, + mem_str(mem), field_type_strs[type]); + goto abort_due_to_error; } + int cmp = mem_cast_implicit_number(mem, type); + is_ge = is_ge || cmp < 0; + /* + * If number before cast were more than max possible for given + * field type, than there is no point to use iterator since we + * won't find anything. + */ + is_zero = is_zero || (is_mem_num_max(mem) && cmp > 0); } - seek_not_found: - assert(pOp->p2>0); - VdbeBranchTaken(res!=0,2); - if (res) { + if (is_zero) { + assert(pOp->p2 > 0); + VdbeBranchTaken(1, 2); goto jump_to_p2; - } else if (eqOnly) { - assert(pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT); - pOp++; /* Skip the OP_IdxLt or OP_IdxGT that follows */ } + if (is_ge) + cur->uc.pCursor->iter_type = ITER_GE; + + int res; + if (sql_cursor_seek(cur->uc.pCursor, mems, len, &res) != 0) + goto abort_due_to_error; + assert((res != 0) == (cur->uc.pCursor->eState == CURSOR_INVALID)); + cur->cacheStatus = CACHE_STALE; +#ifdef SQL_TEST + sql_search_count++; +#endif + assert(pOp->p2 > 0); + VdbeBranchTaken(res, 2); + if (res != 0) + goto jump_to_p2; break; } @@ -2912,7 +2965,9 @@ case OP_Found: { /* jump, in3 */ } } } - rc = sqlCursorMovetoUnpacked(pC->uc.pCursor, pIdxKey, &res); + pC->uc.pCursor->iter_type = ITER_EQ; + rc = sql_cursor_seek(pC->uc.pCursor, pIdxKey->aMem, pIdxKey->nField, + &res); if (pFree != NULL) sqlDbFree(db, pFree); if (rc != 0) @@ -3770,7 +3825,6 @@ case OP_IdxDelete: { VdbeCursor *pC; BtCursor *pCrsr; int res; - UnpackedRecord r; assert(pOp->p3>0); assert(pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem+1 - p->nCursor)+1); @@ -3781,12 +3835,7 @@ case OP_IdxDelete: { pCrsr = pC->uc.pCursor; assert(pCrsr!=0); assert(pOp->p5==0); - r.key_def = pC->key_def; - r.nField = (u16)pOp->p3; - r.default_rc = 0; - r.aMem = &aMem[pOp->p2]; - r.opcode = OP_IdxDelete; - if (sqlCursorMovetoUnpacked(pCrsr, &r, &res) != 0) + if (sql_cursor_seek(pCrsr, &aMem[pOp->p2], (u16)pOp->p3, &res) != 0) goto abort_due_to_error; if (res==0) { assert(pCrsr->eState == CURSOR_VALID); -- 2.25.1