コード例 #1
0
ファイル: subquery.go プロジェクト: billhongs/cockroach
func (v *subqueryVisitor) Visit(expr parser.Expr, pre bool) (parser.Visitor, parser.Expr) {
	if v.pErr != nil {
		return nil, expr
	}
	if !pre {
		v.path = v.path[:len(v.path)-1]
		return nil, expr
	}
	v.path = append(v.path, expr)

	var exists *parser.ExistsExpr
	subquery, ok := expr.(*parser.Subquery)
	if !ok {
		exists, ok = expr.(*parser.ExistsExpr)
		if !ok {
			return v, expr
		}
		subquery, ok = exists.Subquery.(*parser.Subquery)
		if !ok {
			return v, expr
		}
	}

	// Calling makePlan() might recursively invoke expandSubqueries, so we need a
	// copy of the planner in order for there to have a separate subqueryVisitor.
	planMaker := *v.planner
	var plan planNode
	if plan, v.pErr = planMaker.makePlan(subquery.Select, false); v.pErr != nil {
		return nil, expr
	}

	if exists != nil {
		// For EXISTS expressions, all we want to know is if there is at least one
		// result.
		if plan.Next() {
			return v, parser.DBool(true)
		}
		v.pErr = plan.PErr()
		if v.pErr != nil {
			return nil, expr
		}
		return v, parser.DBool(false)
	}

	columns, multipleRows := v.getSubqueryContext()
	if n := len(plan.Columns()); columns != n {
		switch columns {
		case 1:
			v.pErr = roachpb.NewUErrorf("subquery must return only one column, found %d", n)
		default:
			v.pErr = roachpb.NewUErrorf("subquery must return %d columns, found %d", columns, n)
		}
		return nil, expr
	}

	var result parser.Expr
	if multipleRows {
		var rows parser.DTuple
		for plan.Next() {
			values := plan.Values()
			switch len(values) {
			case 1:
				// This seems hokey, but if we don't do this then the subquery expands
				// to a tuple of tuples instead of a tuple of values and an expression
				// like "k IN (SELECT foo FROM bar)" will fail because we're comparing
				// a single value against a tuple.
				rows = append(rows, values[0])
			default:
				// The result from plan.Values() is only valid until the next call to
				// plan.Next(), so make a copy.
				valuesCopy := make(parser.DTuple, len(values))
				copy(valuesCopy, values)
				rows = append(rows, valuesCopy)
			}
		}
		rows.Normalize()
		result = rows
	} else {
		result = parser.DNull
		for plan.Next() {
			values := plan.Values()
			switch len(values) {
			case 1:
				result = values[0]
			default:
				valuesCopy := make(parser.DTuple, len(values))
				copy(valuesCopy, values)
				result = valuesCopy
			}
			if plan.Next() {
				v.pErr = roachpb.NewUErrorf("more than one row returned by a subquery used as an expression")
				return nil, expr
			}
		}
	}

	v.pErr = plan.PErr()
	if v.pErr != nil {
		return nil, expr
	}
	return v, result
}
コード例 #2
0
ファイル: subquery.go プロジェクト: JKhawaja/cockroach
func (s *subquery) doEval() (parser.Datum, error) {
	var result parser.Datum
	switch s.execMode {
	case execModeExists:
		// For EXISTS expressions, all we want to know is if there is at least one
		// result.
		next, err := s.plan.Next()
		if s.err = err; err != nil {
			return result, err
		}
		if next {
			result = parser.MakeDBool(true)
		}
		if result == nil {
			result = parser.MakeDBool(false)
		}

	case execModeAllRows:
		var rows parser.DTuple
		next, err := s.plan.Next()
		for ; next; next, err = s.plan.Next() {
			values := s.plan.Values()
			switch len(values) {
			case 1:
				// This seems hokey, but if we don't do this then the subquery expands
				// to a tuple of tuples instead of a tuple of values and an expression
				// like "k IN (SELECT foo FROM bar)" will fail because we're comparing
				// a single value against a tuple.
				rows = append(rows, values[0])
			default:
				// The result from plan.Values() is only valid until the next call to
				// plan.Next(), so make a copy.
				valuesCopy := make(parser.DTuple, len(values))
				copy(valuesCopy, values)
				rows = append(rows, &valuesCopy)
			}
		}
		if s.err = err; err != nil {
			return result, err
		}
		if s.wantNormalized {
			rows.Normalize()
		}
		result = &rows

	case execModeOneRow:
		result = parser.DNull
		next, err := s.plan.Next()
		if s.err = err; err != nil {
			return result, err
		}
		if next {
			values := s.plan.Values()
			switch len(values) {
			case 1:
				result = values[0]
			default:
				valuesCopy := make(parser.DTuple, len(values))
				copy(valuesCopy, values)
				result = &valuesCopy
			}
			another, err := s.plan.Next()
			if s.err = err; err != nil {
				return result, err
			}
			if another {
				s.err = fmt.Errorf("more than one row returned by a subquery used as an expression")
				return result, s.err
			}
		}
	}

	return result, nil
}
コード例 #3
0
ファイル: subquery.go プロジェクト: GitGoldie/cockroach
func (v *subqueryVisitor) VisitPre(expr parser.Expr) (recurse bool, newExpr parser.Expr) {
	if v.err != nil {
		return false, expr
	}
	v.path = append(v.path, expr)

	var exists *parser.ExistsExpr
	subquery, ok := expr.(*parser.Subquery)
	if !ok {
		exists, ok = expr.(*parser.ExistsExpr)
		if !ok {
			return true, expr
		}
		subquery, ok = exists.Subquery.(*parser.Subquery)
		if !ok {
			return true, expr
		}
	}

	// Calling makePlan() might recursively invoke expandSubqueries, so we need a
	// copy of the planner in order for there to have a separate subqueryVisitor.
	// TODO(nvanbenschoten) We should propagate a desired type here.
	// TODO(knz) the instantiation of the subquery's select node should be moved
	//     to the TypeCheck() method once the prepare and execute phase are separated
	//     for select nodes.
	planMaker := *v.planner
	plan, err := planMaker.makePlan(subquery.Select, nil, false)
	if err != nil {
		return false, expr
	}

	if v.evalCtx.PrepareOnly {
		return false, expr
	}

	if v.err = plan.Start(); v.err != nil {
		return false, expr
	}

	if exists != nil {
		// For EXISTS expressions, all we want to know is if there is at least one
		// result.
		if plan.Next() {
			return true, parser.MakeDBool(true)
		}
		v.err = plan.Err()
		if v.err != nil {
			return false, expr
		}
		return true, parser.MakeDBool(false)
	}

	columns, multipleRows := v.getSubqueryContext()
	if n := len(plan.Columns()); columns != n {
		switch columns {
		case 1:
			v.err = fmt.Errorf("subquery must return only one column, found %d", n)
		default:
			v.err = fmt.Errorf("subquery must return %d columns, found %d", columns, n)
		}
		return true, expr
	}

	var result parser.Expr
	if multipleRows {
		var rows parser.DTuple
		for plan.Next() {
			values := plan.Values()
			switch len(values) {
			case 1:
				// This seems hokey, but if we don't do this then the subquery expands
				// to a tuple of tuples instead of a tuple of values and an expression
				// like "k IN (SELECT foo FROM bar)" will fail because we're comparing
				// a single value against a tuple.
				rows = append(rows, values[0])
			default:
				// The result from plan.Values() is only valid until the next call to
				// plan.Next(), so make a copy.
				valuesCopy := make(parser.DTuple, len(values))
				copy(valuesCopy, values)
				rows = append(rows, &valuesCopy)
			}
		}
		rows.Normalize()
		result = &rows
	} else {
		result = parser.DNull
		for plan.Next() {
			values := plan.Values()
			switch len(values) {
			case 1:
				result = values[0]
			default:
				valuesCopy := make(parser.DTuple, len(values))
				copy(valuesCopy, values)
				result = &valuesCopy
			}
			if plan.Next() {
				v.err = fmt.Errorf("more than one row returned by a subquery used as an expression")
				return false, expr
			}
		}
	}

	v.err = plan.Err()
	if v.err != nil {
		return false, expr
	}
	return true, result
}
コード例 #4
0
ファイル: subquery.go プロジェクト: nporsche/cockroach
func (v *subqueryVisitor) Visit(expr parser.Expr, pre bool) (parser.Visitor, parser.Expr) {
	if v.err != nil {
		return nil, expr
	}
	if !pre {
		v.path = v.path[:len(v.path)-1]
		return nil, expr
	}
	v.path = append(v.path, expr)

	subquery, ok := expr.(*parser.Subquery)
	if !ok {
		return v, expr
	}

	var plan planNode
	if plan, v.err = v.makePlan(subquery.Select); v.err != nil {
		return nil, expr
	}

	columns, multipleRows := v.getSubqueryContext()
	if n := len(plan.Columns()); columns != n {
		switch columns {
		case 1:
			v.err = fmt.Errorf("subquery must return only one column, found %d", n)
		default:
			v.err = fmt.Errorf("subquery must return %d columns, found %d", columns, n)
		}
		return nil, expr
	}

	var result parser.Expr
	if multipleRows {
		var rows parser.DTuple
		for plan.Next() {
			values := plan.Values()
			switch len(values) {
			case 1:
				// This seems hokey, but if we don't do this then the subquery expands
				// to a tuple of tuples instead of a tuple of values and an expression
				// like "k IN (SELECT foo FROM bar)" will fail because we're comparing
				// a single value against a tuple.
				rows = append(rows, values[0])
			default:
				// The result from plan.Values() is only valid until the next call to
				// plan.Next(), so make a copy.
				valuesCopy := make(parser.DTuple, len(values))
				copy(valuesCopy, values)
				rows = append(rows, valuesCopy)
			}
		}
		rows.Normalize()
		result = rows
	} else {
		result = parser.DNull
		for plan.Next() {
			values := plan.Values()
			switch len(values) {
			case 1:
				result = values[0]
			default:
				valuesCopy := make(parser.DTuple, len(values))
				copy(valuesCopy, values)
				result = valuesCopy
			}
			if plan.Next() {
				v.err = fmt.Errorf("more than one row returned by a subquery used as an expression")
				return nil, expr
			}
		}
	}

	v.err = plan.Err()
	if v.err != nil {
		return nil, expr
	}
	return v, result
}