(e *ast.BinaryExpr, inType super.Type)
| 588 | } |
| 589 | |
| 590 | func (t *translator) binaryExpr(e *ast.BinaryExpr, inType super.Type) (sem.Expr, super.Type) { |
| 591 | if e, typ := t.regexp(e, inType); e != nil { |
| 592 | return e, typ |
| 593 | } |
| 594 | op := strings.ToLower(e.Op) |
| 595 | if op == "." { |
| 596 | return t.dot(e, inType) |
| 597 | } |
| 598 | lhs, lhsType := t.expr(e.LHS, inType) |
| 599 | rhs, rhsType := t.expr(e.RHS, inType) |
| 600 | if op == "like" || op == "not like" { |
| 601 | s, ok := t.mustEvalString(rhs) |
| 602 | if !ok { |
| 603 | t.error(e.RHS, errors.New("non-constant pattern for LIKE not supported")) |
| 604 | return badExpr, t.checker.unknown |
| 605 | } |
| 606 | pattern := likeexpr.ToRegexp(s, '\\', false) |
| 607 | expr := &sem.RegexpSearchExpr{ |
| 608 | Node: e, |
| 609 | Pattern: "(?s)" + pattern, |
| 610 | Expr: lhs, |
| 611 | } |
| 612 | if op == "not like" { |
| 613 | return &sem.UnaryExpr{ |
| 614 | Node: e, |
| 615 | Op: "!", |
| 616 | Operand: expr, |
| 617 | }, super.TypeBool |
| 618 | } |
| 619 | return expr, super.TypeBool |
| 620 | } |
| 621 | if op == "in" || op == "not in" { |
| 622 | if q, ok := rhs.(*sem.SubqueryExpr); ok { |
| 623 | q.Array = true |
| 624 | } |
| 625 | } |
| 626 | switch op { |
| 627 | case "=": |
| 628 | op = "==" |
| 629 | case "<>": |
| 630 | op = "!=" |
| 631 | case "||": |
| 632 | t.stringy(e.LHS, lhsType) |
| 633 | t.stringy(e.RHS, rhsType) |
| 634 | return &sem.CallExpr{ |
| 635 | Node: e, |
| 636 | Tag: "concat", |
| 637 | Args: []sem.Expr{lhs, rhs}, |
| 638 | }, super.TypeString |
| 639 | case "not in": |
| 640 | t.checker.in(e, e.LHS, e.RHS, lhsType, rhsType) |
| 641 | return sem.NewUnaryExpr(e, "!", sem.NewBinaryExpr(e, "in", lhs, rhs)), super.TypeBool |
| 642 | case "::": |
| 643 | return &sem.CallExpr{ |
| 644 | Node: e, |
| 645 | Tag: "cast", |
| 646 | Args: []sem.Expr{lhs, rhs}, |
| 647 | }, t.checker.unknown //XXX need cast logic in checker |
no test coverage detected