From: "n.pettik" <korablev@tarantool.org> To: tarantool-patches@freelists.org Cc: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Subject: [tarantool-patches] Re: [PATCH 3/3] sql: fix passing FP values to integer iterator Date: Thu, 6 Jun 2019 16:51:26 +0300 [thread overview] Message-ID: <D052D0C4-C16C-4FE5-8664-696A6D0B6630@tarantool.org> (raw) In-Reply-To: <91e73947-30b8-c172-f52c-be109034e0dd@tarantool.org> >> @@ -1128,8 +1129,9 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W >> } >> } >> /* Inequality constraint comes always at the end of list. */ >> - if (pRangeStart != NULL && >> - idx_def->key_def->parts[nEq].type == FIELD_TYPE_INTEGER) >> + enum field_type ineq_type = idx_def->key_def->parts[nEq].type; >> + if (pRangeStart != NULL && (ineq_type == FIELD_TYPE_INTEGER || >> + ineq_type == FIELD_TYPE_UNSIGNED)) > > 1. Please, add a test for unsigned. I thought that it is quite obvious - > everything needs a test, especially if the bug was found during a review. Surely, forgot about that: diff --git a/test/sql/types.result b/test/sql/types.result index bba39e320..383f93c1a 100644 --- a/test/sql/types.result +++ b/test/sql/types.result @@ -1012,3 +1012,35 @@ box.execute("SELECT a FROM t1 WHERE a IS NULL AND b IS NULL;") box.space.T1:drop() --- ... +format = {} +--- +... +format[1] = { name = 'ID', type = 'unsigned' } +--- +... +format[2] = { name = 'A', type = 'unsigned' } +--- +... +s = box.schema.create_space('T1', { format = format }) +--- +... +_ = s:create_index('pk') +--- +... +_ = s:create_index('sk', { parts = { 'A' } }) +--- +... +s:insert({ 1, 1 }) +--- +- [1, 1] +... +box.execute("SELECT a FROM t1 WHERE a IN (1.1, 2.1);") +--- +- metadata: + - name: A + type: unsigned + rows: [] +... +s:drop() +--- +... diff --git a/test/sql/types.test.lua b/test/sql/types.test.lua index 16448436d..0218c5498 100644 --- a/test/sql/types.test.lua +++ b/test/sql/types.test.lua @@ -247,3 +247,14 @@ box.execute("SELECT a FROM t1 WHERE a = 1.5 AND b IS NULL;") box.execute("SELECT a FROM t1 WHERE a IS NULL AND b IS NULL;") box.space.T1:drop() + +format = {} +format[1] = { name = 'ID', type = 'unsigned' } +format[2] = { name = 'A', type = 'unsigned' } +s = box.schema.create_space('T1', { format = format }) +_ = s:create_index('pk') +_ = s:create_index('sk', { parts = { 'A' } }) +s:insert({ 1, 1 }) +box.execute("SELECT a FROM t1 WHERE a IN (1.1, 2.1);") + +s:drop() >>>> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c >>>> index 977c0fced..e779729c7 100644 >>>> --- a/src/box/sql/wherecode.c >>>> +++ b/src/box/sql/wherecode.c >>>> @@ -1086,31 +1086,61 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W >>>> start_constraints = 1; >>>> } >>>> struct index_def *idx_pk = space->index[0]->def; >>>> - int fieldno = idx_pk->key_def->parts[0].fieldno; >>>> - >>>> uint32_t pk_part_count = idx_pk->key_def->part_count; >>>> - if (pk_part_count == 1 && >>>> - space->def->fields[fieldno].type == FIELD_TYPE_INTEGER) { >>>> - /* Right now INTEGER PRIMARY KEY is the only option to >>>> - * get Tarantool's INTEGER column type. Need special handling >>>> - * here: try to loosely convert FLOAT to INT. If RHS type >>>> - * is not INT or FLOAT - skip this ites, i.e. goto addrNxt. >>>> - */ >>>> - int limit = pRangeStart == NULL ? nEq : nEq + 1; >>>> - for (int i = 0; i < limit; i++) { >>>> - if (idx_def->key_def->parts[i].fieldno == >>>> - idx_pk->key_def->parts[0].fieldno) { >>>> - /* Here: we know for sure that table has INTEGER >>>> - PRIMARY KEY, single column, and Index we're >>>> - trying to use for scan contains this column. */ >>>> - if (i < nEq) >>>> - sqlVdbeAddOp2(v, OP_MustBeInt, regBase + i, addrNxt); >>>> - else >>>> - force_integer_reg = regBase + i; >>>> - break; >>>> - } >>>> + /* >>>> + * Tarantool's iterator over integer fields doesn't >>>> + * tolerate floating point values. Hence, if term >>>> + * is equality comparison and value of operand is >>>> + * not integer, we can skip it since it always >>>> + * results in false: INT a == 0.5 -> false; >>>> + * It is done using OP_MustBeInt facilities. >>>> + * In case term is greater comparison (a > ?), we >>>> + * should notify OP_SeekGT to process truncation of >>>> + * floating point value: a > 0.5 -> a >= 1; >>>> + * It is done by setting P5 flag for OP_Seek*. >>>> + * It is worth mentioning that we do not need >>>> + * this step when it comes for less comparison: >>>> + * key is NULL in this case: values are ordered >>>> + * as NULL, ... NULL, min_value, ..., so to >>>> + * fetch min value we pass NULL to GT iterator. >>> >>> 4. I did not get the 'less comparator == compare with NULL' >>> idea. Firstly you describe how to compare 'a > ?', this is >>> fully understandable. Then you, as I see, go to the 'a < ?' >>> case, but somewhy say, that it is the same as 'NULL < ?'. >>> Do not understand. >> >> I say that in this case key which is passed to iterator is NULL >> and type of iterator is GE. > > 2. Why? I still do not understand. What if I wrote 'a < 100'? you > do not replace 100 with NULL. But the comment says, that for any > 'less' operator a key is NULL: "when it comes for less comparison > key is NULL”. Consider example: box.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT UNIQUE);") box.execute("INSERT INTO t1 VALUES (1, 1), (2, 2), (3, -1), (4, NULL), (5, NULL);”) box.execute("SELECT a FROM t1 WHERE a < 100;”) Bytecode for this query is: 102> 0 Init 0 1 0 00 Start at 1 102> 1 IteratorOpen 1 1 0 space<name=T1> 00 index id = 1, space ptr = space<name=T1> or reg[0]; unique_unnamed_T1_2 102> 2 Explain 0 0 0 SEARCH TABLE T1 USING COVERING INDEX unique_unnamed_T1_2 (A<?) 00 102> 3 Null 0 1 0 00 r[1]=NULL 102> 4 SeekGT 1 10 1 1 00 key=r[1] 102> 5 Integer 100 1 0 00 r[1]=100 102> 6 IdxGE 1 10 1 1 00 key=r[1] 102> 7 Column 1 1 2 00 r[2]=T1.A 102> 8 ResultRow 2 1 0 00 output=r[2] 102> 9 Next 1 6 0 00 102> 10 Halt 0 0 0 00 As you can see, SeekGT accepts NULL as a key for iterator. Then we iterate until search condition is true. So, basically 100 is replaced with NULL and not used as a key of iterator. I wanted to underline this fact in my comment. >> Consequently, pRangeStart is NULL. >> I’ve slightly updated this part of comment: >> >> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c >> index 3025e5e6b..01ee9ce90 100644 >> --- a/src/box/sql/wherecode.c >> +++ b/src/box/sql/wherecode.c >> @@ -1089,10 +1089,10 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W >> * floating point value: a > 0.5 -> a >= 1; >> * It is done by setting P5 flag for OP_Seek*. >> * It is worth mentioning that we do not need >> - * this step when it comes for less comparison: >> - * key is NULL in this case: values are ordered >> - * as NULL, ... NULL, min_value, ..., so to >> - * fetch min value we pass NULL to GT iterator. >> + * this step when it comes for less (<) comparison >> + * of nullable field. Key is NULL in this case: >> + * values are ordered as NULL, ... NULL, min_value, >> + * so to fetch min value we pass NULL to GT iterator. >> >> >> @@ -1112,24 +1112,33 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W >> /* >> * OP_MustBeInt consider NULLs as >> * non-integer values, so firstly >> * check whether value is NULL or not. >> */ >> - seek_addr = sqlVdbeAddOp1(v, OP_IsNull, >> + seek_addrs[i] = sqlVdbeAddOp1(v, OP_IsNull, >> regBase); > > 3. Now the indentation is broken. diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c index fde2efee0..4bd14bd5f 100644 --- a/src/box/sql/wherecode.c +++ b/src/box/sql/wherecode.c @@ -1130,7 +1130,7 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W * check whether value is NULL or not. */ seek_addrs[i] = sqlVdbeAddOp1(v, OP_IsNull, - regBase); + regBase); sqlVdbeAddOp2(v, OP_MustBeInt, regBase + i, addrNxt); start_types[i] = FIELD_TYPE_SCALAR;
next prev parent reply other threads:[~2019-06-06 13:51 UTC|newest] Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-05-24 17:39 [tarantool-patches] [PATCH 0/3] Fix passing key with different from iterator's type Nikita Pettik 2019-05-24 17:39 ` [tarantool-patches] [PATCH 1/3] sql: remove redundant check of space format from QP Nikita Pettik 2019-05-24 17:39 ` [tarantool-patches] [PATCH 2/3] sql: remove redundant type derivation " Nikita Pettik 2019-05-27 21:49 ` [tarantool-patches] " Vladislav Shpilevoy 2019-05-28 19:58 ` n.pettik 2019-05-24 17:39 ` [tarantool-patches] [PATCH 3/3] sql: fix passing FP values to integer iterator Nikita Pettik 2019-05-25 5:51 ` [tarantool-patches] " Konstantin Osipov 2019-05-27 21:49 ` Vladislav Shpilevoy 2019-05-28 7:19 ` Konstantin Osipov 2019-05-28 11:31 ` Vladislav Shpilevoy 2019-05-29 7:02 ` Konstantin Osipov 2019-05-28 13:00 ` n.pettik 2019-05-29 9:14 ` Konstantin Osipov 2019-05-27 21:49 ` Vladislav Shpilevoy 2019-05-28 19:58 ` n.pettik 2019-06-02 18:11 ` Vladislav Shpilevoy 2019-06-06 13:51 ` n.pettik [this message] 2019-06-06 19:07 ` Vladislav Shpilevoy 2019-07-11 9:19 ` [tarantool-patches] Re: [PATCH 0/3] Fix passing key with different from iterator's type Kirill Yukhin
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=D052D0C4-C16C-4FE5-8664-696A6D0B6630@tarantool.org \ --to=korablev@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=v.shpilevoy@tarantool.org \ --subject='[tarantool-patches] Re: [PATCH 3/3] sql: fix passing FP values to integer iterator' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox