From: "n.pettik" <korablev@tarantool.org> To: tarantool-patches@freelists.org Cc: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Subject: [tarantool-patches] Re: [PATCH 9/9] sql: make <search condition> accept only boolean Date: Tue, 23 Apr 2019 22:59:02 +0300 [thread overview] Message-ID: <09F94053-CCE0-49FD-8788-3F0749BF7ED8@tarantool.org> (raw) In-Reply-To: <d6dfdee9-c395-286f-716a-dd89895d87e9@tarantool.org> > On 18/04/2019 20:55, n.pettik wrote: >> >>>> <search condition> is a predicate used as a part of WHERE and >>>> JOIN clauses. ANSI SQL states that <search condition> must >>>> accept only boolean arguments. In our SQL it is implemented as >>>> bytecode instruction OP_If which in turn carries out logic of >>>> conditional jump. Since it can be involved in executing other routines >>>> different from <search condition>, >>> >>> 1. Which other routines? What is a valid case of OP_If with non-boolean >>> value in check? >> >> For instance, to verify that register containing LIMIT value is > 0. > > Yes, and this is almost the only case. What is more, it happens only once > per request, to check if LIMIT == 0 initially. Further it is decremented > and checked via OP_IfNotZero and OP_DecrJumpZero. > >> It is quite hard to track values which come to this opcode, so we >> can’t be sure that it always accepts booleans. > > It is hard, but without it > > 1) You can't be sure, that really all the search conditions > are checked to be booleans; > > 2) It makes OP_If/IfNot slower, and they are called repeatedly in > requests; One branching worth nothing. Below you suggest to split opcode into two (fix me if I’m wrong), which in turn affects performance way much more. > 3) It adds one more flag SQL_BOOLREQ, which looks very crutchy. IMHO it is matter of taste. Anyway, removed this flag. > I tried myself to disable non-bool for OP_If/IfNot, and I fixed > LIMIT cases. Then I've found CASE-WHEN also uses OP_If/IfNot in > this distorted way. > > tarantool> box.execute("SELECT CASE WHEN 0 THEN 'zero' WHEN 1 THEN 'one' END;”) Fixed. > It violates the standard. "Information technology — > Database languages — SQL — Part 2: Foundation (SQL/Foundation)", > 2011, page 230. > > 'WHEN' is a search condition, but I've used '1', not 'true'. > Also I tested it on PostgreSQL - they raise an error, so it is > both standard and practically used way. > > Below are my fixes for LIMIT and a small obvious refactoring, > but they are *not on the branch* - not all the tests pass when I > start banning non-bools in OP_If/IfNot. I’ve fixed that. > And I do not like these temporary registers. They seem OK to me. > Probably we could somehow reuse OP_IfNotZero, > for example, via having it refactored to OP_CheckZero which would > be able to jump both if the target is zero and if not. For example, > it could look like this: > > /** > * P1 - a register with value to check if it equals zero. > * P3 - if 1, then check r[P1] == 0, if 0, then check r[P1] != 0. > * If the check is successful, then jump to P2. > * > * if (r[P1] == 0) == P3 then jump p2 > */ > > But I do not know what to do with its current version decrementing > r[P1]. My ideas were about splitting 'decrement' operation from > zero-checking. Then we could do these replacements: > > OP_IfNotZero -> OP_CheckZero, OP_Decrement > > OP_DecrJumpZero -> OP_Decrement, OP_CheckZero > > OP_IfNot -> OP_CheckZero (where it is used inappropriately) > > And now we drop OP_IfNotZero and OP_DecrJumpZero, and forbid > non-bool in OP_IfNot/OP_If. Perhaps we will find more standard > violations. > > ================================================================== I’ve managed to make OP_If accept only booleans without splitting opcodes. Diff (including your changes): diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index 3c4cde2c8..86fc28606 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -1094,24 +1094,16 @@ sqlPExprAddSelect(Parse * pParse, Expr * pExpr, Select * pSelect) * LEFT JOIN, then we cannot determine at compile-time whether or not * is it true or false, so always return 0. */ -static int +static inline bool exprAlwaysTrue(Expr * p) { - if (ExprHasProperty(p, EP_FromJoin)) - return 0; - if (p->op == TK_TRUE) - return 1; - return 0; + return !ExprHasProperty(p, EP_FromJoin) && p->op == TK_TRUE; } -static int +static inline bool exprAlwaysFalse(Expr * p) { - if (ExprHasProperty(p, EP_FromJoin)) - return 0; - if (p->op == TK_FALSE) - return 1; - return 0; + return !ExprHasProperty(p, EP_FromJoin) && p->op == TK_FALSE; } struct Expr * diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 428e3a92f..280391040 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -769,8 +769,10 @@ pushOntoSorter(Parse * pParse, /* Parser context */ pParse->nMem += pSort->nOBSat; nKey = nExpr - pSort->nOBSat + bSeq; if (bSeq) { - addrFirst = - sqlVdbeAddOp1(v, OP_IfNot, regBase + nExpr); + int r1 = sqlGetTempReg(pParse); + sqlVdbeAddOp2(v, OP_Integer, 0, r1); + addrFirst = sqlVdbeAddOp3(v, OP_Eq, r1, 0, regBase + nExpr); + sqlReleaseTempReg(pParse, r1); } else { addrFirst = sqlVdbeAddOp1(v, OP_SequenceTest, @@ -799,9 +801,10 @@ pushOntoSorter(Parse * pParse, /* Parser context */ pSort->labelBkOut); sqlVdbeAddOp1(v, OP_ResetSorter, pSort->iECursor); if (iLimit) { - sqlVdbeAddOp2(v, OP_IfNot, iLimit, - pSort->labelDone); - VdbeCoverage(v); + int r1 = sqlGetTempReg(pParse); + sqlVdbeAddOp2(v, OP_Integer, 0, r1); + sqlVdbeAddOp3(v, OP_Eq, r1, pSort->labelDone, iLimit); + sqlReleaseTempReg(pParse, r1); } sqlVdbeJumpHere(v, addrFirst); sqlExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat); @@ -2115,11 +2118,10 @@ computeLimitRegisters(Parse * pParse, Select * p, int iBreak) P4_STATIC); sqlVdbeResolveLabel(v, positive_limit_label); - sqlReleaseTempReg(pParse, r1); VdbeCoverage(v); VdbeComment((v, "LIMIT counter")); - sqlVdbeAddOp2(v, OP_IfNot, iLimit, iBreak); - VdbeCoverage(v); + sqlVdbeAddOp3(v, OP_Eq, r1, iBreak, iLimit); + sqlReleaseTempReg(pParse, r1); if ((p->selFlags & SF_SingleRow) != 0) { if (ExprHasProperty(p->pLimit, EP_System)) { @@ -2673,10 +2675,11 @@ multiSelect(Parse * pParse, /* Parsing context */ p->iLimit = pPrior->iLimit; p->iOffset = pPrior->iOffset; if (p->iLimit) { - addr = - sqlVdbeAddOp1(v, OP_IfNot, - p->iLimit); - VdbeCoverage(v); + int r1 = sqlGetTempReg(pParse); + sqlVdbeAddOp2(v, OP_Integer, 0, r1); + addr = sqlVdbeAddOp3(v, OP_Eq, r1, 0, + p->iLimit); + sqlReleaseTempReg(pParse, r1); VdbeComment((v, "Jump ahead if LIMIT reached")); if (p->iOffset) { @@ -3047,7 +3050,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p, sqlVdbeJumpHere(v, addr1); sqlVdbeAddOp3(v, OP_Copy, in->iSdst, reg_prev + 1, in->nSdst - 1); - sqlVdbeAddOp2(v, OP_Integer, 1, reg_prev); + sqlVdbeAddOp2(v, OP_Bool, true, reg_prev); } if (parse->db->mallocFailed) return 0; @@ -3370,7 +3373,7 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */ assert(nOrderBy >= expr_count || db->mallocFailed); regPrev = pParse->nMem + 1; pParse->nMem += expr_count + 1; - sqlVdbeAddOp2(v, OP_Integer, 0, regPrev); + sqlVdbeAddOp2(v, OP_Bool, 0, regPrev); key_info_dup = sql_key_info_new(db, expr_count); if (key_info_dup != NULL) { for (int i = 0; i < expr_count; i++) { diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 51d30ee1b..b22598b67 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -1721,8 +1721,8 @@ integer_overflow: * functions. * * If P1 is not zero, then it is a register that a subsequent min() or - * max() aggregate will set to 1 if the current row is not the minimum or - * maximum. The P1 register is initialized to 0 by this instruction. + * max() aggregate will set to true if the current row is not the minimum or + * maximum. The P1 register is initialized to false by this instruction. * * The interface used by the implementation of the aforementioned functions * to retrieve the collation sequence set by this opcode is not available @@ -1731,7 +1731,7 @@ integer_overflow: case OP_CollSeq: { assert(pOp->p4type==P4_COLLSEQ || pOp->p4.pColl == NULL); if (pOp->p1) { - sqlVdbeMemSetInt64(&aMem[pOp->p1], 0); + mem_set_bool(&aMem[pOp->p1], false); } break; } @@ -2563,21 +2563,10 @@ case OP_IfNot: { /* jump, in1 */ } else if ((pIn1->flags & MEM_Bool) != 0) { c = pOp->opcode==OP_IfNot ? ! pIn1->u.b : pIn1->u.b; } else { - if (pOp->p5 == OPFLAG_BOOLREQ) { - diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_text(pIn1), "boolean"); - rc = SQL_TARANTOOL_ERROR; - goto abort_due_to_error; - } - double v; - if (sqlVdbeRealValue(pIn1, &v) != 0) { - diag_set(ClientError, ER_SQL_TYPE_MISMATCH, - sql_value_text(pIn1), "real"); - rc = SQL_TARANTOOL_ERROR; - goto abort_due_to_error; - } - c = v != 0; - if (pOp->opcode==OP_IfNot) c = !c; + diag_set(ClientError, ER_SQL_TYPE_MISMATCH, + sql_value_text(pIn1), "boolean"); + rc = SQL_TARANTOOL_ERROR; + goto abort_due_to_error; } VdbeBranchTaken(c!=0, 2); if (c) { @@ -5301,7 +5290,7 @@ case OP_AggStep: { if (pCtx->skipFlag) { assert(pOp[-1].opcode==OP_CollSeq); i = pOp[-1].p1; - if (i) sqlVdbeMemSetInt64(&aMem[i], 1); + if (i) mem_set_bool(&aMem[i], true); } break; } diff --git a/test/sql-tap/cse.test.lua b/test/sql-tap/cse.test.lua index 39c1cc4ca..78d0d2046 100755 --- a/test/sql-tap/cse.test.lua +++ b/test/sql-tap/cse.test.lua @@ -102,7 +102,7 @@ test:do_execsql_test( test:do_execsql_test( "cse-1.6.3", [[ - SELECT CASE WHEN b THEN d WHEN e THEN f ELSE 999 END, b, c, d FROM t1 + SELECT CASE WHEN CAST(b AS BOOLEAN) THEN d WHEN CAST(e AS BOOLEAN) THEN f ELSE 999 END, b, c, d FROM t1 ]], { -- <cse-1.6.3> 13, 11, 12, 13, 23, 21, 22, 23 @@ -112,7 +112,7 @@ test:do_execsql_test( test:do_execsql_test( "cse-1.6.4", [[ - SELECT b, c, d, CASE WHEN b THEN d WHEN e THEN f ELSE 999 END FROM t1 + SELECT b, c, d, CASE WHEN CAST(b AS BOOLEAN) THEN d WHEN CAST(e AS BOOLEAN) THEN f ELSE 999 END FROM t1 ]], { -- <cse-1.6.4> 11, 12, 13, 13, 21, 22, 23, 23 @@ -122,7 +122,7 @@ test:do_execsql_test( test:do_execsql_test( "cse-1.6.5", [[ - SELECT b, c, d, CASE WHEN 0 THEN d WHEN e THEN f ELSE 999 END FROM t1 + SELECT b, c, d, CASE WHEN false THEN d WHEN CAST(e AS BOOLEAN) THEN f ELSE 999 END FROM t1 ]], { -- <cse-1.6.5> 11, 12, 13, 15, 21, 22, 23, 25 diff --git a/test/sql-tap/e_select1.test.lua b/test/sql-tap/e_select1.test.lua index 9e9c4c551..c4724e636 100755 --- a/test/sql-tap/e_select1.test.lua +++ b/test/sql-tap/e_select1.test.lua @@ -703,7 +703,7 @@ test:do_execsql_test( test:do_execsql_test( "e_select-3.2.1b", [[ - SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k + SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k <> 0 ]], { -- <e_select-3.2.1b> 1, 3, 5 diff --git a/test/sql-tap/orderby1.test.lua b/test/sql-tap/orderby1.test.lua index ea03c494b..51e8d301f 100755 --- a/test/sql-tap/orderby1.test.lua +++ b/test/sql-tap/orderby1.test.lua @@ -735,7 +735,7 @@ test:do_execsql_test( SELECT ( SELECT 'hardware' FROM ( SELECT 'software' ORDER BY 'firmware' ASC, 'sportswear' DESC - ) GROUP BY 1 HAVING length(b) + ) GROUP BY 1 HAVING length(b) <> 0 ) FROM abc; ]], { diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua index 1bad7679f..6beeb34cb 100755 --- a/test/sql-tap/select1.test.lua +++ b/test/sql-tap/select1.test.lua @@ -1492,13 +1492,13 @@ test:do_catchsql_test( -- </select1-7.9> }) -test:do_execsql_test( +test:do_catchsql_test( "select1-8.1", [[ SELECT f1 FROM test1 WHERE 4.3+2.4 OR 1 ORDER BY f1 ]], { -- <select1-8.1> - 11, 33 + 1, 'Type mismatch: can not convert 6.7 to boolean' -- </select1-8.1> }) diff --git a/test/sql-tap/select2.test.lua b/test/sql-tap/select2.test.lua index 7f0188d6f..e08c8b3b6 100755 --- a/test/sql-tap/select2.test.lua +++ b/test/sql-tap/select2.test.lua @@ -222,7 +222,7 @@ test:do_execsql_test( test:do_execsql_test( "select2-4.3", [[ - SELECT * FROM aa CROSS JOIN bb WHERE NOT b; + SELECT * FROM aa CROSS JOIN bb WHERE NOT b <> 0; ]], { -- <select2-4.3> 1, 0, 3, 0 @@ -242,7 +242,7 @@ test:do_execsql_test( test:do_execsql_test( "select2-4.5", [[ - SELECT * FROM aa, bb WHERE NOT min(a,b); + SELECT * FROM aa, bb WHERE NOT min(a,b) <> 0; ]], {
next prev parent reply other threads:[~2019-04-23 19:59 UTC|newest] Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-04-14 15:03 [tarantool-patches] [PATCH 0/9] Introduce type BOOLEAN in SQL Nikita Pettik 2019-04-14 15:03 ` [tarantool-patches] [PATCH 1/9] sql: refactor mem_apply_numeric_type() Nikita Pettik 2019-04-14 15:04 ` [tarantool-patches] [PATCH 2/9] sql: disallow text values participate in sum() aggregate Nikita Pettik 2019-04-16 14:12 ` [tarantool-patches] " Vladislav Shpilevoy 2019-04-18 17:54 ` n.pettik 2019-04-22 18:02 ` Vladislav Shpilevoy 2019-04-23 19:58 ` n.pettik 2019-04-14 15:04 ` [tarantool-patches] [PATCH 3/9] sql: use msgpack types instead of custom ones Nikita Pettik 2019-04-16 14:12 ` [tarantool-patches] " Vladislav Shpilevoy 2019-04-18 17:54 ` n.pettik 2019-04-22 18:02 ` Vladislav Shpilevoy 2019-04-23 19:58 ` n.pettik 2019-04-14 15:04 ` [tarantool-patches] [PATCH 4/9] sql: introduce type boolean Nikita Pettik 2019-04-16 14:12 ` [tarantool-patches] " Vladislav Shpilevoy 2019-04-18 17:54 ` n.pettik 2019-04-22 18:02 ` Vladislav Shpilevoy 2019-04-23 19:58 ` n.pettik 2019-04-23 21:06 ` Vladislav Shpilevoy 2019-04-14 15:04 ` [tarantool-patches] [PATCH 5/9] sql: improve type determination for column meta Nikita Pettik 2019-04-16 14:12 ` [tarantool-patches] " Vladislav Shpilevoy 2019-04-18 17:54 ` n.pettik 2019-04-22 18:02 ` Vladislav Shpilevoy 2019-04-23 19:58 ` n.pettik 2019-04-14 15:04 ` [tarantool-patches] [PATCH 6/9] sql: make comparison predicate return boolean Nikita Pettik 2019-04-16 14:12 ` [tarantool-patches] " Vladislav Shpilevoy 2019-04-18 17:54 ` n.pettik 2019-04-14 15:04 ` [tarantool-patches] [PATCH 7/9] sql: make predicates accept and " Nikita Pettik 2019-04-16 14:12 ` [tarantool-patches] " Vladislav Shpilevoy 2019-04-18 17:55 ` n.pettik 2019-04-14 15:04 ` [tarantool-patches] [PATCH 9/9] sql: make <search condition> accept only boolean Nikita Pettik 2019-04-16 14:12 ` [tarantool-patches] " Vladislav Shpilevoy 2019-04-18 17:55 ` n.pettik 2019-04-22 18:02 ` Vladislav Shpilevoy 2019-04-23 19:59 ` n.pettik [this message] 2019-04-23 21:06 ` Vladislav Shpilevoy 2019-04-23 22:01 ` n.pettik [not found] ` <b2a84f129c2343d3da3311469cbb7b20488a21c2.1555252410.git.korablev@tarantool.org> 2019-04-16 14:12 ` [tarantool-patches] Re: [PATCH 8/9] sql: make LIKE predicate return boolean result Vladislav Shpilevoy 2019-04-18 17:55 ` n.pettik 2019-04-22 18:02 ` Vladislav Shpilevoy 2019-04-23 19:58 ` n.pettik 2019-04-24 10:28 ` [tarantool-patches] Re: [PATCH 0/9] Introduce type BOOLEAN in SQL Vladislav Shpilevoy 2019-04-25 8:46 ` 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=09F94053-CCE0-49FD-8788-3F0749BF7ED8@tarantool.org \ --to=korablev@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=v.shpilevoy@tarantool.org \ --subject='[tarantool-patches] Re: [PATCH 9/9] sql: make <search condition> accept only boolean' \ /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