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 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 }