func isMixedTypeComparison(c *parser.ComparisonExpr) bool { switch c.Operator { case parser.In, parser.NotIn: tuple := *c.Right.(*parser.DTuple) for _, expr := range tuple { if !sameTypeExprs(c.TypedLeft(), expr.(parser.TypedExpr)) { return true } } return false default: return !sameTypeExprs(c.TypedLeft(), c.TypedRight()) } }
func simplifyOneAndInExpr(left, right *parser.ComparisonExpr) (parser.TypedExpr, parser.TypedExpr) { if left.Operator != parser.In && right.Operator != parser.In { panic(fmt.Sprintf("IN expression required: %s vs %s", left, right)) } origLeft, origRight := left, right switch left.Operator { case parser.EQ, parser.NE, parser.GT, parser.GE, parser.LT, parser.LE, parser.Is: switch right.Operator { case parser.In: left, right = right, left } fallthrough case parser.In: ltuple := *left.Right.(*parser.DTuple) switch right.Operator { case parser.Is: if right.Right == parser.DNull { return parser.MakeDBool(false), nil } case parser.EQ, parser.NE, parser.GT, parser.GE, parser.LT, parser.LE: // Our tuple will be sorted (see simplifyComparisonExpr). Binary search // for the right datum. datum := right.Right.(parser.Datum) i := sort.Search(len(ltuple), func(i int) bool { return ltuple[i].(parser.Datum).Compare(datum) >= 0 }) switch right.Operator { case parser.EQ: if i < len(ltuple) && ltuple[i].Compare(datum) == 0 { return right, nil } return parser.MakeDBool(false), nil case parser.NE: if i < len(ltuple) && ltuple[i].Compare(datum) == 0 { if len(ltuple) < 2 { return parser.MakeDBool(false), nil } ltuple = remove(ltuple, i) } return parser.NewTypedComparisonExpr( parser.In, left.TypedLeft(), <uple, ), nil case parser.GT: if i < len(ltuple) { if ltuple[i].Compare(datum) == 0 { ltuple = ltuple[i+1:] } else { ltuple = ltuple[i:] } if len(ltuple) > 0 { return parser.NewTypedComparisonExpr( parser.In, left.TypedLeft(), <uple, ), nil } } return parser.MakeDBool(false), nil case parser.GE: if i < len(ltuple) { ltuple = ltuple[i:] if len(ltuple) > 0 { return parser.NewTypedComparisonExpr( parser.In, left.TypedLeft(), <uple, ), nil } } return parser.MakeDBool(false), nil case parser.LT: if i < len(ltuple) { if i == 0 { return parser.MakeDBool(false), nil } ltuple = ltuple[:i] return parser.NewTypedComparisonExpr( parser.In, left.TypedLeft(), <uple, ), nil } return left, nil case parser.LE: if i < len(ltuple) { if ltuple[i].Compare(datum) == 0 { i++ } if i == 0 { return parser.MakeDBool(false), nil } ltuple = ltuple[:i] return parser.NewTypedComparisonExpr( parser.In, left.TypedLeft(), <uple, ), nil } return left, nil } case parser.In: // Both of our tuples are sorted. Intersect the lists. rtuple := *right.Right.(*parser.DTuple) intersection := intersectSorted(ltuple, rtuple) if len(*intersection) == 0 { return parser.MakeDBool(false), nil } return parser.NewTypedComparisonExpr( parser.In, left.TypedLeft(), intersection, ), nil } } return origLeft, origRight }
func simplifyComparisonExpr(n *parser.ComparisonExpr) (parser.TypedExpr, bool) { // NormalizeExpr will have left comparisons in the form "<var> <op> // <datum>" unless they could not be simplified further in which case // simplifyExpr cannot handle them. For example, "lower(a) = 'foo'" left, right := n.TypedLeft(), n.TypedRight() if isVar(left) && isDatum(right) { if right == parser.DNull { switch n.Operator { case parser.IsNotDistinctFrom: switch left.(type) { case *qvalue, *parser.IndexedVar: // Transform "a IS NOT DISTINCT FROM NULL" into "a IS NULL". return parser.NewTypedComparisonExpr( parser.Is, left, right, ), true } case parser.IsDistinctFrom: switch left.(type) { case *qvalue, *parser.IndexedVar: // Transform "a IS DISTINCT FROM NULL" into "a IS NOT NULL". return parser.NewTypedComparisonExpr( parser.IsNot, left, right, ), true } case parser.Is, parser.IsNot: switch left.(type) { case *qvalue, *parser.IndexedVar: // "a IS {,NOT} NULL" can be used during index selection to restrict // the range of scanned keys. return n, true } default: // All of the remaining comparison operators have the property that when // comparing to NULL they evaluate to NULL (see evalComparisonOp). NULL is // not the same as false, but in the context of a WHERE clause, NULL is // considered not-true which is the same as false. return parser.MakeDBool(false), true } } switch n.Operator { case parser.EQ: // Translate "(a, b) = (1, 2)" to "(a, b) IN ((1, 2))". switch left.(type) { case *parser.Tuple: return parser.NewTypedComparisonExpr( parser.In, left, &parser.DTuple{right.(parser.Datum)}, ), true } return n, true case parser.NE, parser.GE, parser.LE: return n, true case parser.GT: // This simplification is necessary so that subsequent transformation of // > constraint to >= can use Datum.Next without concern about whether a // next value exists. Note that if the variable (n.Left) is NULL, this // comparison would evaluate to NULL which is equivalent to false for a // boolean expression. if right.(parser.Datum).IsMax() { return parser.MakeDBool(false), true } return n, true case parser.LT: // Note that if the variable is NULL, this would evaluate to NULL which // would equivalent to false for a boolean expression. if right.(parser.Datum).IsMin() { return parser.MakeDBool(false), true } return n, true case parser.In, parser.NotIn: tuple := *right.(*parser.DTuple) if len(tuple) == 0 { return parser.MakeDBool(false), true } return n, true case parser.Like: // a LIKE 'foo%' -> a >= "foo" AND a < "fop" if d, ok := right.(*parser.DString); ok { if i := strings.IndexAny(string(*d), "_%"); i >= 0 { return makePrefixRange((*d)[:i], left, false), false } return makePrefixRange(*d, left, true), false } // TODO(pmattis): Support parser.DBytes? case parser.SimilarTo: // a SIMILAR TO "foo.*" -> a >= "foo" AND a < "fop" if d, ok := right.(*parser.DString); ok { pattern := parser.SimilarEscape(string(*d)) if re, err := regexp.Compile(pattern); err == nil { prefix, complete := re.LiteralPrefix() return makePrefixRange(parser.DString(prefix), left, complete), false } } // TODO(pmattis): Support parser.DBytes? } } return parser.MakeDBool(true), false }
func simplifyOneOrInExpr(left, right *parser.ComparisonExpr) (parser.TypedExpr, parser.TypedExpr) { if left.Operator != parser.In && right.Operator != parser.In { panic(fmt.Sprintf("IN expression required: %s vs %s", left, right)) } origLeft, origRight := left, right switch left.Operator { case parser.EQ, parser.NE, parser.GT, parser.GE, parser.LT, parser.LE: switch right.Operator { case parser.In: left, right = right, left } fallthrough case parser.In: tuple := *left.Right.(*parser.DTuple) switch right.Operator { case parser.EQ: datum := right.Right.(parser.Datum) // We keep the tuples for an IN expression in sorted order. So now we just // merge the two sorted lists. return parser.NewTypedComparisonExpr( parser.In, left.TypedLeft(), mergeSorted(tuple, parser.DTuple{datum}), ), nil case parser.NE, parser.GT, parser.GE, parser.LT, parser.LE: datum := right.Right.(parser.Datum) i := sort.Search(len(tuple), func(i int) bool { return tuple[i].(parser.Datum).Compare(datum) >= 0 }) switch right.Operator { case parser.NE: if i < len(tuple) && tuple[i].Compare(datum) == 0 { return makeIsNotNull(right.TypedLeft()), nil } return right, nil case parser.GT: if i == 0 { // datum >= tuple[0] if tuple[i].Compare(datum) == 0 { // datum = tuple[0] return parser.NewTypedComparisonExpr( parser.GE, left.TypedLeft(), datum, ), nil } return right, nil } case parser.GE: if i == 0 { // datum >= tuple[0] return right, nil } case parser.LT: if i == len(tuple) { // datum > tuple[len(tuple)-1] return right, nil } else if i == len(tuple)-1 { // datum >= tuple[len(tuple)-1] if tuple[i].Compare(datum) == 0 { // datum == tuple[len(tuple)-1] return parser.NewTypedComparisonExpr( parser.LE, left.TypedLeft(), datum, ), nil } } case parser.LE: if i == len(tuple) || (i == len(tuple)-1 && tuple[i].Compare(datum) == 0) { // datum >= tuple[len(tuple)-1] return right, nil } } case parser.In: // We keep the tuples for an IN expression in sorted order. So now we // just merge the two sorted lists. return parser.NewTypedComparisonExpr( parser.In, left.TypedLeft(), mergeSorted(tuple, *right.Right.(*parser.DTuple)), ), nil } } return origLeft, origRight }