func (this *DNF) VisitFunction(expr expression.Function) (interface{}, error) { var exp expression.Expression = expr switch expr := expr.(type) { case *expression.IsBoolean: exp = expression.NewLE(expr.Operand(), expression.TRUE_EXPR) case *expression.IsNumber: exp = expression.NewAnd( expression.NewGT(expr.Operand(), expression.TRUE_EXPR), expression.NewLT(expr.Operand(), expression.EMPTY_STRING_EXPR)) case *expression.IsString: exp = expression.NewAnd( expression.NewGE(expr.Operand(), expression.EMPTY_STRING_EXPR), expression.NewLT(expr.Operand(), expression.EMPTY_ARRAY_EXPR)) case *expression.IsArray: exp = expression.NewAnd( expression.NewGE(expr.Operand(), expression.EMPTY_ARRAY_EXPR), expression.NewLT(expr.Operand(), _EMPTY_OBJECT_EXPR)) case *expression.IsObject: // Not equivalent to IS OBJECT. Includes BINARY values. exp = expression.NewGE(expr.Operand(), _EMPTY_OBJECT_EXPR) } return exp, exp.MapChildren(this) }
/* Constrain the WHERE condition to reflect the aggregate query. For example: SELECT AVG(v) FROM widget w; is rewritten as: SELECT AVG(v) FROM widget w WHERE v IS NOT NULL; This enables the query to use an index on v. */ func constrainAggregate(cond expression.Expression, aggs map[string]algebra.Aggregate) expression.Expression { var first expression.Expression for _, agg := range aggs { if first == nil { first = agg.Operand() if first == nil { return cond } continue } op := agg.Operand() if op == nil || !first.EquivalentTo(op) { return cond } } if first == nil { return cond } var constraint expression.Expression = expression.NewIsNotNull(first) if cond != nil { constraint = expression.NewAnd(cond, constraint) } return constraint }
func (this *DNF) VisitBetween(expr *expression.Between) (interface{}, error) { err := expr.MapChildren(this) if err != nil { return nil, err } return expression.NewAnd(expression.NewGE(expr.First(), expr.Second()), expression.NewLE(expr.First(), expr.Third())), nil }
func newSubsetLike(expr expression.BinaryFunction, re *regexp.Regexp) expression.Visitor { if re == nil { // Pattern is not a constant return newSubsetDefault(expr) } prefix, complete := re.LiteralPrefix() if complete { eq := expression.NewEq(expr.First(), expression.NewConstant(prefix)) return newSubsetEq(eq.(*expression.Eq)) } if prefix == "" { return newSubsetDefault(expr) } var and expression.Expression le := expression.NewLE(expression.NewConstant(prefix), expr.First()) last := len(prefix) - 1 if prefix[last] < math.MaxUint8 { bytes := []byte(prefix) bytes[last]++ and = expression.NewAnd(le, expression.NewLT( expr.First(), expression.NewConstant(string(bytes)))) } else { and = expression.NewAnd(le, expression.NewLT( expr.First(), expression.EMPTY_ARRAY_EXPR)) } sand := newSubsetAnd(and.(*expression.And)) rv := &subsetLike{} rv.test = func(expr2 expression.Expression) (bool, error) { if expr.EquivalentTo(expr2) { return true, nil } return sand.test(expr2) } return rv }
/* Bounded DNF, to mitigate combinatorial worst-case. Internally apply Disjunctive Normal Form. Convert ANDs of ORs to ORs of ANDs. For example: (A OR B) AND C => (A AND C) OR (B AND C) */ func applyDNF(expr *expression.And, level int) expression.Expression { na := len(expr.Operands()) if na > 4 { return expr } for i, aterm := range expr.Operands() { switch aterm := aterm.(type) { case *expression.Or: no := len(aterm.Operands()) if no*na > 8 { return expr } oterms := make(expression.Expressions, no) for j, oterm := range aterm.Operands() { aterms := make(expression.Expressions, na) for ii, atrm := range expr.Operands() { if ii == i { aterms[ii] = oterm } else { aterms[ii] = atrm } } if level > 2 { oterms[j] = expression.NewAnd(aterms...) } else { oterms[j] = applyDNF(expression.NewAnd(aterms...), level+1) } } rv := expression.NewOr(oterms...) return rv } } return expr }
func (this *DNF) VisitLike(expr *expression.Like) (interface{}, error) { err := expr.MapChildren(this) if err != nil { return nil, err } re := expr.Regexp() if re == nil { return expr, nil } prefix, complete := re.LiteralPrefix() if complete { eq := expression.NewEq(expr.First(), expression.NewConstant(prefix)) return eq, nil } if prefix == "" { return expr, nil } var and expression.Expression le := expression.NewLE(expression.NewConstant(prefix), expr.First()) last := len(prefix) - 1 if prefix[last] < math.MaxUint8 { bytes := []byte(prefix) bytes[last]++ and = expression.NewAnd(le, expression.NewLT( expr.First(), expression.NewConstant(string(bytes)))) } else { and = expression.NewAnd(le, expression.NewLT( expr.First(), expression.EMPTY_ARRAY_EXPR)) } return and, nil }
func (this *builder) buildJoinScan(keyspace datastore.Keyspace, node *algebra.KeyspaceTerm, op string) ( datastore.Index, expression.Covers, error) { indexes, err := allIndexes(keyspace) if err != nil { return nil, nil, err } var pred expression.Expression pred = expression.NewIsNotNull(node.Keys().Copy()) dnf := NewDNF() pred, err = dnf.Map(pred) if err != nil { return nil, nil, err } subset := pred if this.where != nil { subset = expression.NewAnd(subset, this.where.Copy()) subset, err = dnf.Map(subset) if err != nil { return nil, nil, err } } formalizer := expression.NewFormalizer() formalizer.Keyspace = node.Alias() primaryKey := expression.Expressions{ expression.NewField( expression.NewMeta(expression.NewConstant(node.Alias())), expression.NewFieldName("id", false)), } sargables, err := sargableIndexes(indexes, pred, subset, primaryKey, dnf, formalizer) if err != nil { return nil, nil, err } minimals, err := minimalIndexes(sargables, pred) if err != nil { return nil, nil, err } if len(minimals) == 0 { return nil, nil, errors.NewNoIndexJoinError(node.Alias(), op) } return this.buildCoveringJoinScan(minimals, node, op) }
func (this *DNF) VisitNot(expr *expression.Not) (interface{}, error) { err := expr.MapChildren(this) if err != nil { return nil, err } var exp expression.Expression = expr switch operand := expr.Operand().(type) { case *expression.Not: exp = operand.Operand() case *expression.And: operands := make(expression.Expressions, len(operand.Operands())) for i, op := range operand.Operands() { operands[i] = expression.NewNot(op) } exp = expression.NewOr(operands...) case *expression.Or: operands := make(expression.Expressions, len(operand.Operands())) for i, op := range operand.Operands() { operands[i] = expression.NewNot(op) } and := expression.NewAnd(operands...) return this.VisitAnd(and) case *expression.Eq: exp = expression.NewOr(expression.NewLT(operand.First(), operand.Second()), expression.NewLT(operand.Second(), operand.First())) case *expression.LT: exp = expression.NewLE(operand.Second(), operand.First()) case *expression.LE: exp = expression.NewLT(operand.Second(), operand.First()) default: return expr, nil } return exp, exp.MapChildren(this) }
/* Apply Disjunctive Normal Form. Convert ANDs of ORs to ORs of ANDs. For example: (A OR B) AND C => (A AND C) OR (B AND C) Also apply constant folding. Remove any constant terms. */ func (this *DNF) VisitAnd(expr *expression.And) (interface{}, error) { err := expr.MapChildren(this) if err != nil { return nil, err } // Constant folding var terms expression.Expressions for _, term := range expr.Operands() { val := term.Value() if val == nil { if terms == nil { terms = make(expression.Expressions, 0, len(expr.Operands())) } terms = append(terms, term) continue } if !val.Truth() { return expression.FALSE_EXPR, nil } } if len(terms) == 0 { return expression.TRUE_EXPR, nil } if len(terms) < len(expr.Operands()) { expr = expression.NewAnd(terms...) } // DNF if dnfComplexity(expr, 16) >= 16 { return expr, nil } else { return applyDNF(expr, 0), nil } }