コード例 #1
0
ファイル: analyze_test.go プロジェクト: gechong/cockroach
func checkEquivExpr(a, b parser.Expr, qvals qvalMap) error {
	// The expressions above only use the values 1 and 2. Verify that the
	// simplified expressions evaluate to the same value as the original
	// expression for interesting values.
	zero := parser.DInt(0)
	for _, v := range []parser.Datum{zero, zero + 1, zero + 2, zero + 3, parser.DNull} {
		for _, q := range qvals {
			q.datum = v
		}
		da, err := a.Eval(parser.EvalContext{})
		if err != nil {
			return fmt.Errorf("%s: %v", a, err)
		}
		db, err := b.Eval(parser.EvalContext{})
		if err != nil {
			return fmt.Errorf("%s: %v", b, err)
		}
		// This is tricky: we don't require the expressions to produce identical
		// results, but to either both return true or both return not true (either
		// false or NULL).
		if (da == parser.DBool(true)) != (db == parser.DBool(true)) {
			return fmt.Errorf("%s: %s: expected %s, but found %s", a, v, da, db)
		}
	}
	return nil
}
コード例 #2
0
ファイル: group.go プロジェクト: guowenfei-mathsfan/cockroach
func (v *extractAggregatesVisitor) Visit(expr parser.Expr, pre bool) (parser.Visitor, parser.Expr) {
	if !pre || v.err != nil {
		return nil, expr
	}

	// This expression is in the GROUP BY - switch to the copy that will accept
	// qvalues for this and any subtrees.
	if _, ok := v.groupStrs[expr.String()]; ok && v.groupedCopy != nil {
		v = v.groupedCopy
	}

	switch t := expr.(type) {
	case *parser.FuncExpr:
		if len(t.Name.Indirect) > 0 {
			break
		}
		if impl, ok := aggregates[strings.ToLower(string(t.Name.Base))]; ok {
			if len(t.Exprs) != 1 {
				// Type checking has already run on these expressions thus
				// if an aggregate function of the wrong arity gets here,
				// something has gone really wrong.
				panic(fmt.Sprintf("%s has %d arguments (expected 1)", t.Name.Base, len(t.Exprs)))
			}

			f := &aggregateFunc{
				expr:    t,
				arg:     t.Exprs[0],
				create:  impl,
				group:   v.n,
				buckets: make(map[string]aggregateImpl),
			}
			if t.Type == parser.Distinct {
				f.seen = make(map[string]struct{})
			}
			v.n.funcs = append(v.n.funcs, f)
			return nil, f
		}
	case *qvalue:
		if v.groupedCopy != nil {
			v.err = fmt.Errorf("column \"%s\" must appear in the GROUP BY clause or be used in an aggregate function",
				t.colRef.get().Name)
			return v, expr
		}
		f := &aggregateFunc{
			expr:    t,
			arg:     t,
			create:  newIdentAggregate,
			group:   v.n,
			buckets: make(map[string]aggregateImpl),
		}
		v.n.funcs = append(v.n.funcs, f)
		return nil, f
	}
	return v, expr
}
コード例 #3
0
ファイル: select.go プロジェクト: bogdanbatog/cockroach
func (s *selectNode) addRender(target parser.SelectExpr) *roachpb.Error {
	// outputName will be empty if the target is not aliased.
	outputName := string(target.As)

	if isStar, cols, exprs, err := checkRenderStar(target, &s.table, s.qvals); err != nil {
		s.pErr = roachpb.NewError(err)
		return s.pErr
	} else if isStar {
		s.columns = append(s.columns, cols...)
		s.render = append(s.render, exprs...)
		return nil
	}

	// When generating an output column name it should exactly match the original
	// expression, so determine the output column name before we perform any
	// manipulations to the expression.
	outputName = getRenderColName(target)

	// Resolve qualified names. This has the side-effect of normalizing any
	// qualified name found.
	var resolved parser.Expr
	var err error
	if resolved, err = s.resolveQNames(target.Expr); err != nil {
		s.pErr = roachpb.NewError(err)
		return s.pErr
	}
	if resolved, s.pErr = s.planner.expandSubqueries(resolved, 1); s.pErr != nil {
		return s.pErr
	}
	var typ parser.Datum
	typ, err = resolved.TypeCheck(s.planner.evalCtx.Args)
	s.pErr = roachpb.NewError(err)
	if s.pErr != nil {
		return s.pErr
	}
	var normalized parser.Expr
	normalized, err = s.planner.parser.NormalizeExpr(s.planner.evalCtx, resolved)
	s.pErr = roachpb.NewError(err)
	if s.pErr != nil {
		return s.pErr
	}
	s.render = append(s.render, normalized)

	if target.As == "" {
		switch t := target.Expr.(type) {
		case *parser.QualifiedName:
			// If the expression is a qualified name, use the column name, not the
			// full qualification as the column name to return.
			outputName = t.Column()
		}
	}
	s.columns = append(s.columns, ResultColumn{Name: outputName, Typ: typ})
	return nil
}
コード例 #4
0
func sameTypeExprs(left, right parser.Expr) (bool, error) {
	dummyLeft, err := left.TypeCheck(nil)
	if err != nil || dummyLeft == parser.DNull {
		return false, err
	}
	dummyRight, err := right.TypeCheck(nil)
	if err != nil || dummyRight == parser.DNull {
		return false, err
	}
	return !dummyLeft.TypeEqual(dummyRight), nil
}
コード例 #5
0
ファイル: expr_filter.go プロジェクト: petermattis/cockroach
// runFilter runs a filter expression and returs whether the filter passes.
func runFilter(filter parser.Expr, evalCtx parser.EvalContext) (bool, error) {
	if filter == nil {
		return true, nil
	}

	d, err := filter.Eval(evalCtx)
	if err != nil {
		return false, err
	}

	return d != parser.DNull && bool(d.(parser.DBool)), nil
}
コード例 #6
0
ファイル: group.go プロジェクト: kaustubhkurve/cockroach
func (v *checkAggregateVisitor) Visit(expr parser.Expr, pre bool) (parser.Visitor, parser.Expr) {
	if !pre || v.aggrErr != nil {
		return nil, expr
	}

	switch t := expr.(type) {
	case *parser.FuncExpr:
		if _, ok := aggregates[strings.ToLower(string(t.Name.Base))]; ok {
			return nil, expr
		}
	case *qvalue:
		if _, ok := v.groupStrs[t.String()]; ok {
			return nil, expr
		}
		v.aggrErr = fmt.Errorf("column \"%s\" must appear in the GROUP BY clause or be used in an aggregate function", t.col.Name)
		return v, expr
	}

	if _, ok := v.groupStrs[expr.String()]; ok {
		return nil, expr
	}
	return v, expr
}
コード例 #7
0
ファイル: scan.go プロジェクト: surpass/cockroach
func (n *scanNode) addRender(target parser.SelectExpr) *roachpb.Error {
	// When generating an output column name it should exactly match the original
	// expression, so determine the output column name before we perform any
	// manipulations to the expression (such as star expansion).
	var outputName string
	if target.As != "" {
		outputName = string(target.As)
	} else {
		outputName = target.Expr.String()
	}

	// If a QualifiedName has a StarIndirection suffix we need to match the
	// prefix of the qualified name to one of the tables in the query and
	// then expand the "*" into a list of columns.
	if qname, ok := target.Expr.(*parser.QualifiedName); ok {
		if n.pErr = roachpb.NewError(qname.NormalizeColumnName()); n.pErr != nil {
			return n.pErr
		}
		if qname.IsStar() {
			if n.desc == nil {
				return roachpb.NewUErrorf("\"%s\" with no tables specified is not valid", qname)
			}
			if target.As != "" {
				return roachpb.NewUErrorf("\"%s\" cannot be aliased", qname)
			}
			tableName := qname.Table()
			if tableName != "" && !equalName(n.desc.Alias, tableName) {
				return roachpb.NewUErrorf("table \"%s\" not found", tableName)
			}

			if n.isSecondaryIndex {
				for _, id := range n.index.ColumnIDs {
					var col *ColumnDescriptor
					if col, n.pErr = n.desc.FindColumnByID(id); n.pErr != nil {
						return n.pErr
					}
					qval := n.getQVal(*col)
					n.columns = append(n.columns, resultColumn{name: col.Name, typ: qval.datum})
					n.render = append(n.render, qval)
				}
			} else {
				for _, col := range n.desc.VisibleColumns() {
					qval := n.getQVal(col)
					n.columns = append(n.columns, resultColumn{name: col.Name, typ: qval.datum})
					n.render = append(n.render, qval)
				}
			}
			return nil
		}
	}

	// Resolve qualified names. This has the side-effect of normalizing any
	// qualified name found.
	var resolved parser.Expr
	if resolved, n.pErr = n.resolveQNames(target.Expr); n.pErr != nil {
		return n.pErr
	}
	if resolved, n.pErr = n.planner.expandSubqueries(resolved, 1); n.pErr != nil {
		return n.pErr
	}
	var typ parser.Datum
	var err error
	typ, err = resolved.TypeCheck(n.planner.evalCtx.Args)
	n.pErr = roachpb.NewError(err)
	if n.pErr != nil {
		return n.pErr
	}
	var normalized parser.Expr
	normalized, err = n.planner.parser.NormalizeExpr(n.planner.evalCtx, resolved)
	n.pErr = roachpb.NewError(err)
	if n.pErr != nil {
		return n.pErr
	}
	n.render = append(n.render, normalized)

	if target.As == "" {
		switch t := target.Expr.(type) {
		case *parser.QualifiedName:
			// If the expression is a qualified name, use the column name, not the
			// full qualification as the column name to return.
			outputName = t.Column()
		}
	}
	n.columns = append(n.columns, resultColumn{name: outputName, typ: typ})
	return nil
}
コード例 #8
0
ファイル: expr_filter.go プロジェクト: binlijin/cockroach
// splitBoolExpr splits a boolean expression E into two boolean expressions RES and REM such that:
//
//  - RES only has variables known to the conversion function (it is "restricted" to a particular
//    set of variables)
//
//  - If weaker is true, for any setting of variables x:
//       E(x) = (RES(x) AND REM(x))
//    This implies RES(x) <= E(x), i.e. RES is "weaker"
//
//  - If weaker is false:
//       E(x) = (RES(x) OR REM(x))
//    This implies RES(x) => E(x), i.e. RES is "stronger"
//
// Note: the original expression is modified in-place and should not be used again.
func splitBoolExpr(expr parser.Expr, conv varConvertFunc, weaker bool) (restricted, remainder parser.Expr) {
	// If the expression only contains "restricted" vars, the split is trivial.
	if exprCheckVars(expr, conv) {
		// An "empty" filter is always true in the weaker (normal) case (where the filter is
		// equivalent to RES AND REM) and always false in the stronger (inverted) case (where the
		// filter is equivalent to RES OR REM).
		return exprConvertVars(expr, conv), parser.DBool(weaker)
	}

	switch t := expr.(type) {
	case *parser.AndExpr:
		if weaker {
			// In the weaker (normal) case, we have
			//   E = (leftRes AND leftRem) AND (rightRes AND rightRem)
			// We can just rearrange:
			//   E = (leftRes AND rightRes) AND (leftRem AND rightRem)
			leftRes, leftRem := splitBoolExpr(t.Left, conv, weaker)
			rightRes, rightRem := splitBoolExpr(t.Right, conv, weaker)
			return makeAnd(leftRes, rightRes), makeAnd(leftRem, rightRem)
		}

		// In the stronger (inverted) case, we have
		//   E = (leftRes OR leftRem) AND (rightRes OR rightRem)
		// We can't do more than:
		//   E = (leftRes AND rightRes) OR E
		exprCopy := expr.DeepCopy()
		leftRes, _ := splitBoolExpr(t.Left, conv, weaker)
		rightRes, _ := splitBoolExpr(t.Right, conv, weaker)
		return makeAnd(leftRes, rightRes), exprCopy

	case *parser.OrExpr:
		if !weaker {
			// In the stronger (inverted) case, we have
			//   E = (leftRes OR leftRem) OR (rightRes AND rightRem)
			// We can just rearrange:
			//   E = (leftRes OR rightRes) OR (leftRem AND rightRem)
			leftRes, leftRem := splitBoolExpr(t.Left, conv, weaker)
			rightRes, rightRem := splitBoolExpr(t.Right, conv, weaker)
			return makeOr(leftRes, rightRes), makeOr(leftRem, rightRem)
		}

		// In the weaker (normal) case, we have
		//   E = (leftRes AND leftRem) OR (rightRes AND rightRem)
		// We can't do more than:
		//   E = (leftRes OR rightRes) OR E
		exprCopy := expr.DeepCopy()
		leftRes, _ := splitBoolExpr(t.Left, conv, weaker)
		rightRes, _ := splitBoolExpr(t.Right, conv, weaker)
		return makeOr(leftRes, rightRes), exprCopy

	case *parser.ParenExpr:
		return splitBoolExpr(t.Expr, conv, weaker)

	case *parser.NotExpr:
		exprRes, exprRem := splitBoolExpr(t.Expr, conv, !weaker)
		return makeNot(exprRes), makeNot(exprRem)

	default:
		// We can't split off anything (we already handled the case when expr contains only
		// restricted vars above).
		// For why we return DBool(weaker), see the comment above on "empty" filters.
		return parser.DBool(weaker), expr
	}
}