func encodeStartConstraintDescending( spans []span, c *parser.ComparisonExpr) { switch c.Operator { case parser.Is: // An IS NULL expressions allows us to constrain the start of the range // to begin at NULL. if c.Right != parser.DNull { panic("Expected NULL operand for IS operator.") } for i := range spans { spans[i].start = encoding.EncodeNullDescending(spans[i].start) } case parser.LE, parser.EQ: if datum, ok := c.Right.(parser.Datum); ok { key, pErr := encodeTableKey(nil, datum, encoding.Descending) if pErr != nil { panic(pErr) } // Append the constraint to all of the existing spans. for i := range spans { spans[i].start = append(spans[i].start, key...) } } case parser.LT: // A "<" constraint is the last start constraint. Since the constraint // is exclusive and the start key is inclusive, we're going to apply // a .PrefixEnd(). if datum, ok := c.Right.(parser.Datum); ok { key, pErr := encodeTableKey(nil, datum, encoding.Descending) if pErr != nil { panic(pErr) } // Append the constraint to all of the existing spans. for i := range spans { spans[i].start = append(spans[i].start, key...) spans[i].start = spans[i].start.PrefixEnd() } } default: panic(fmt.Errorf("unexpected operator: %s", c.String())) } }
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 encodeEndConstraintDescending(spans []span, c *parser.ComparisonExpr, isLastEndConstraint bool) { switch c.Operator { case parser.IsNot: // An IS NULL expressions allows us to constrain the end of the range // to stop at NULL. if c.Right != parser.DNull { panic("Expected NULL operand for IS NOT operator.") } for i := range spans { spans[i].end = encoding.EncodeNotNullDescending(spans[i].end) } case parser.GE, parser.EQ: datum := c.Right.(parser.Datum) for i := range spans { spans[i].end = encodeInclusiveEndValue( spans[i].end, datum, encoding.Descending, isLastEndConstraint) } case parser.GT: panic("'>' operators should have been transformed to '>='.") default: panic(fmt.Errorf("unexpected operator: %s", c.String())) } }
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.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 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 }
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) }