func simplifyComparisonExpr(n *parser.ComparisonExpr) parser.Expr { // 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'" if isVar(n.Left) && isDatum(n.Right) { // All of the comparison operators have the property that when comparing to // NULL they evaulate 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. if n.Right == parser.DNull { return parser.DBool(false) } switch n.Operator { case parser.EQ, parser.NE, parser.GT, parser.GE, parser.LT, parser.LE: return n case parser.In, parser.NotIn: tuple, ok := n.Right.(parser.DTuple) if !ok { break } if !typeCheckTuple(n.Left, tuple) { break } sort.Sort(tuple) tuple = uniqTuple(tuple) if len(tuple) == 0 { return parser.DBool(false) } n.Right = tuple return n case parser.Like: // a LIKE 'foo%' -> a >= "foo" AND a < "fop" if d, ok := n.Right.(parser.DString); ok { if i := strings.IndexAny(string(d), "_%"); i >= 0 { return makePrefixRange(d[:i], n.Left, false) } return makePrefixRange(d, n.Left, true) } case parser.SimilarTo: // a SIMILAR TO "foo.*" -> a >= "foo" AND a < "fop" if d, ok := n.Right.(parser.DString); ok { if re, err := regexp.Compile(string(d)); err == nil { prefix, complete := re.LiteralPrefix() return makePrefixRange(parser.DString(prefix), n.Left, complete) } } } } return parser.DBool(true) }
func simplifyComparisonExpr(n *parser.ComparisonExpr) parser.Expr { // 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'" if isVar(n.Left) && isDatum(n.Right) { switch n.Operator { case parser.EQ, parser.NE, parser.GT, parser.GE, parser.LT, parser.LE: return n case parser.In, parser.NotIn: tuple, ok := n.Right.(parser.DTuple) if !ok { break } if !typeCheckTuple(n.Left, tuple) { break } sort.Sort(tuple) tuple = uniqTuple(tuple) n.Right = tuple return n case parser.Like: // a LIKE 'foo%' -> a >= "foo" AND a < "fop" if d, ok := n.Right.(parser.DString); ok { if i := strings.IndexAny(string(d), "_%"); i >= 0 { return makePrefixRange(d[:i], n.Left, false) } return makePrefixRange(d, n.Left, true) } case parser.SimilarTo: // a SIMILAR TO "foo.*" -> a >= "foo" AND a < "fop" if d, ok := n.Right.(parser.DString); ok { if re, err := regexp.Compile(string(d)); err == nil { prefix, complete := re.LiteralPrefix() return makePrefixRange(parser.DString(prefix), n.Left, complete) } } } } return parser.DBool(true) }
func simplifyComparisonExpr(n *parser.ComparisonExpr) parser.Expr { // 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'" if isVar(n.Left) && isDatum(n.Right) { if n.Right == parser.DNull { switch n.Operator { case parser.IsNotDistinctFrom: switch n.Left.(type) { case *qvalue: // Transform "a IS NOT DISTINCT FROM NULL" into "a IS NULL". return &parser.ComparisonExpr{ Operator: parser.Is, Left: n.Left, Right: n.Right, } } case parser.IsDistinctFrom: switch n.Left.(type) { case *qvalue: // Transform "a IS DISTINCT FROM NULL" into "a IS NOT NULL". return &parser.ComparisonExpr{ Operator: parser.IsNot, Left: n.Left, Right: n.Right, } } case parser.Is, parser.IsNot: switch n.Left.(type) { case *qvalue: // "a IS {,NOT} NULL" can be used during index selection to restrict // the range of scanned keys. return n } 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.DBool(false) } } switch n.Operator { case parser.EQ: // Translate "(a, b) = (1, 2)" to "(a, b) IN ((1, 2))". switch n.Left.(type) { case parser.Tuple: return &parser.ComparisonExpr{ Operator: parser.In, Left: n.Left, Right: parser.DTuple{n.Right.(parser.Datum)}, } } return n case parser.NE, parser.GE, parser.LE: return n 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 n.Right.(parser.Datum).IsMax() { return parser.DBool(false) } return n 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 n.Right.(parser.Datum).IsMin() { return parser.DBool(false) } return n case parser.In, parser.NotIn: tuple := n.Right.(parser.DTuple) sort.Sort(tuple) tuple = uniqTuple(tuple) if len(tuple) == 0 { return parser.DBool(false) } n.Right = tuple return n case parser.Like: // a LIKE 'foo%' -> a >= "foo" AND a < "fop" if d, ok := n.Right.(parser.DString); ok { if i := strings.IndexAny(string(d), "_%"); i >= 0 { return makePrefixRange(d[:i], n.Left, false) } return makePrefixRange(d, n.Left, true) } // TODO(pmattis): Support parser.DBytes? case parser.SimilarTo: // a SIMILAR TO "foo.*" -> a >= "foo" AND a < "fop" if d, ok := n.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), n.Left, complete) } } // TODO(pmattis): Support parser.DBytes? } } return parser.DBool(true) }
func simplifyComparisonExpr(n *parser.ComparisonExpr) parser.Expr { // 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'" if isVar(n.Left) && isDatum(n.Right) { // All of the comparison operators have the property that when comparing to // NULL they evaulate 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. if n.Right == parser.DNull { return parser.DBool(false) } switch n.Operator { case parser.EQ, parser.NE, parser.GE, parser.LE: return n 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 n.Right.(parser.Datum).IsMax() { return parser.DBool(false) } return n 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 n.Right.(parser.Datum).IsMin() { return parser.DBool(false) } return n case parser.In, parser.NotIn: tuple := n.Right.(parser.DTuple) sort.Sort(tuple) tuple = uniqTuple(tuple) if len(tuple) == 0 { return parser.DBool(false) } n.Right = tuple return n case parser.Like: // a LIKE 'foo%' -> a >= "foo" AND a < "fop" if d, ok := n.Right.(parser.DString); ok { if i := strings.IndexAny(string(d), "_%"); i >= 0 { return makePrefixRange(d[:i], n.Left, false) } return makePrefixRange(d, n.Left, true) } case parser.SimilarTo: // a SIMILAR TO "foo.*" -> a >= "foo" AND a < "fop" if d, ok := n.Right.(parser.DString); ok { if re, err := regexp.Compile(string(d)); err == nil { prefix, complete := re.LiteralPrefix() return makePrefixRange(parser.DString(prefix), n.Left, complete) } } } } return parser.DBool(true) }