diff --git a/ast/expressions.go b/ast/expressions.go index 6a894682d..61e523c8b 100644 --- a/ast/expressions.go +++ b/ast/expressions.go @@ -443,6 +443,8 @@ type ExistsSubqueryExpr struct { exprNode // Sel is the subquery, may be rewritten to other type of expression. Sel ExprNode + // Not is true, the expression is "not exists". + Not bool } // Format the ExprNode into a Writer. diff --git a/parser.go b/parser.go index 06f4463d7..fd3dcd8cd 100644 --- a/parser.go +++ b/parser.go @@ -8351,7 +8351,13 @@ yynewstate: } case 263: { - parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Not, V: yyS[yypt-0].expr} + expr, ok := yyS[yypt-0].expr.(*ast.ExistsSubqueryExpr) + if ok { + expr.Not = true + parser.yyVAL.expr = yyS[yypt-0].expr + } else { + parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Not, V: yyS[yypt-0].expr} + } } case 264: { diff --git a/parser.y b/parser.y index 6e7a08e94..da0e3fd86 100644 --- a/parser.y +++ b/parser.y @@ -2568,7 +2568,13 @@ Expression: } | "NOT" Expression %prec not { - $$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2} + expr, ok := $2.(*ast.ExistsSubqueryExpr) + if ok { + expr.Not = true + $$ = $2 + } else { + $$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2} + } } | BoolPri IsOrNotOp trueKwd %prec is { diff --git a/parser_test.go b/parser_test.go index 8fd99b028..8549628a0 100644 --- a/parser_test.go +++ b/parser_test.go @@ -2704,3 +2704,20 @@ func (s *testParserSuite) TestFieldText(c *C) { c.Assert(traceStmt.Stmt.Text(), Equals, "select a from t") } } + +func (s *testParserSuite) TestNotExistsSubquery(c *C) { + table := []testCase{ + {`select * from t1 where not exists (select * from t2 where t1.a = t2.a)`, true}, + } + + parser := New() + for _, tt := range table { + stmt, _, err := parser.Parse(tt.src, "", "") + c.Assert(err, IsNil) + + sel := stmt[0].(*ast.SelectStmt) + exists, ok := sel.Where.(*ast.ExistsSubqueryExpr) + c.Assert(ok, IsTrue) + c.Assert(exists.Not, Equals, tt.ok) + } +}