Example #1
0
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()))
	}
}
Example #2
0
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)
}
Example #3
0
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()))
	}
}
Example #4
0
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())
	}
}
Example #5
0
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)
}
Example #6
0
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(),
					&ltuple,
				), 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(),
							&ltuple,
						), 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(),
							&ltuple,
						), 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(),
						&ltuple,
					), 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(),
						&ltuple,
					), 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
}
Example #7
0
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
}
Example #8
0
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
}
Example #9
0
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)
}