From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> To: "n.pettik" <korablev@tarantool.org>, tarantool-patches@freelists.org Subject: [tarantool-patches] Re: [PATCH 9/9] sql: make <search condition> accept only boolean Date: Mon, 22 Apr 2019 21:02:16 +0300 [thread overview] Message-ID: <d6dfdee9-c395-286f-716a-dd89895d87e9@tarantool.org> (raw) In-Reply-To: <6DC565F1-23AA-49CA-BB2C-AA60EE9E3593@tarantool.org> Thanks for the fixes! 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; 3) It adds one more flag SQL_BOOLREQ, which looks very crutchy. 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;") --- - metadata: - name: CASE WHEN 0 THEN 'zero' WHEN 1 THEN 'one' END type: string rows: - ['one'] ... It violates the standard. "Information technology — Database languages — SQL — Part 2: Foundation (SQL/Foundation)", 2011, page 230. <case expression> ::= <case abbreviation> | <case specification> <case abbreviation> ::= NULLIF <left paren> <value expression> <comma> <value expression> <right paren> | COALESCE <left paren> <value expression> { <comma> <value expression> }... <right paren> <case specification> ::= <simple case> | <searched case> <simple case> ::= CASE <case operand> <simple when clause>... [ <else clause> ] END <searched case> ::= CASE <searched when clause>... [ <else clause> ] END <simple when clause> ::= WHEN <when operand list> THEN <result> <searched when clause> ::= WHEN <search condition> THEN <result> Our case is 'case expression' -> 'case specification' -> 'searched case' -> 'searched when clause' -> 'search condition' -> 'boolean'. '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. And I do not like these temporary registers. 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. ================================================================== 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..3680b6f2b 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,8 +801,10 @@ pushOntoSorter(Parse * pParse, /* Parser context */ pSort->labelBkOut); sqlVdbeAddOp1(v, OP_ResetSorter, pSort->iECursor); if (iLimit) { - sqlVdbeAddOp2(v, OP_IfNot, iLimit, - pSort->labelDone); + int r1 = sqlGetTempReg(pParse); + sqlVdbeAddOp2(v, OP_Integer, 0, r1); + sqlVdbeAddOp3(v, OP_Eq, r1, pSort->labelDone, iLimit); + sqlReleaseTempReg(pParse, r1); VdbeCoverage(v); } sqlVdbeJumpHere(v, addrFirst); @@ -2115,10 +2119,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); + sqlVdbeAddOp3(v, OP_Eq, r1, iBreak, iLimit); + sqlReleaseTempReg(pParse, r1); VdbeCoverage(v); if ((p->selFlags & SF_SingleRow) != 0) { @@ -2673,10 +2677,11 @@ multiSelect(Parse * pParse, /* Parsing context */ p->iLimit = pPrior->iLimit; p->iOffset = pPrior->iOffset; if (p->iLimit) { - addr = - sqlVdbeAddOp1(v, OP_IfNot, - p->iLimit); + int r1 = sqlGetTempReg(pParse); + sqlVdbeAddOp2(v, OP_Integer, 0, r1); + addr = sqlVdbeAddOp3(v, OP_Eq, r1, 0, p->iLimit); VdbeCoverage(v); + sqlReleaseTempReg(pParse, r1); VdbeComment((v, "Jump ahead if LIMIT reached")); if (p->iOffset) {
next prev parent reply other threads:[~2019-04-22 18:02 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 [this message] 2019-04-23 19:59 ` n.pettik 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=d6dfdee9-c395-286f-716a-dd89895d87e9@tarantool.org \ --to=v.shpilevoy@tarantool.org \ --cc=korablev@tarantool.org \ --cc=tarantool-patches@freelists.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