ronantakizawa/codereview-bench huggingface dataset:
Repository: prometheus/prometheus
File: promql/functions.go (Go)

Code before:
		}
	}

	if len(histograms) > 1 {
		prev := histograms[0].H
		for _, sample := range histograms[1:] {
			current := sample.H
			if current.DetectReset(prev) {
				resets++
			}
			prev = current
		}
	}

	return append(enh.Out, Sample{F: float64(resets)}), nil
}

// mergeSeries merges the floats and the histograms in a series
// into a slice of Sample sorted by their timestamp.
// it assumes that the floats and histograms are sorted by their timestamp.
func mergeSeries(series Series) []*Sample {
	floats := series.Floats
	histograms := series.Histograms
	samples := make([]*Sample, 0, len(floats)+len(histograms))

	// i for iterating through floats and j for iterating through histograms
	i, j := 0, 0
	for i < len(floats) && j < len(histograms) {
		if floats[i].T < histograms[j].T {
			samples = append(samples, &Sample{
				F: floats[i].F,
			})
			i++
		} else {
			samples = append(samples, &Sample{
				H: histograms[j].H,
			})
			j++
		}
	}
	// Append the remaining samples
	for i < len(floats) {
		samples = append(samples, &Sample{
			F: floats[i].F,
		})
		i++
	}
	for j < len(histograms) {
		samples = append(samples, &Sample{
			H: histograms[j].H,
		})

Review comment (suggestion):
```suggestion
	// i for iterating through floats and j for iterating through histograms.
```

Code after:
		}
	}

	if len(histograms) > 1 {
		prev := histograms[0].H
		for _, sample := range histograms[1:] {
			current := sample.H
			if current.DetectReset(prev) {
				resets++
			}
			prev = current
		}
	}

	return append(enh.Out, Sample{F: float64(resets)}), nil
}

// === changes(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
func funcChanges(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
	floats := vals[0].(Matrix)[0].Floats
	histograms := vals[0].(Matrix)[0].Histograms
	changes := 0
	if len(floats) == 0 && len(histograms) == 0 {
		return enh.Out, nil
	}

	var prevSample, curSample Sample
	for iFloat, iHistogram := 0, 0; iFloat < len(floats) || iHistogram < len(histograms); {
		switch {
		// Process a float sample if no histogram sample remains or its timestamp is earlier.
		// Process a histogram sample if no float sample remains or its timestamp is earlier.
		case iHistogram >= len(histograms) || iFloat < len(floats) && floats[iFloat].T < histograms[iHistogram].T:
			{
				curSample.F = floats[iFloat].F
				curSample.H = nil
				iFloat++
			}
		case iFloat >= len(floats) || iHistogram < len(histograms) && floats[iFloat].T > histograms[iHistogram].T:
			{
				curSample.H = histograms[iHistogram].H
				iHistogram++
			}
		}
		// Skip the comparison for the first sample, just initialize prevSample.
		if iFloat+iHistogram == 1 {
			prevSample = curSample
			continue
		}
		switch {
		case prevSample.H == nil && curSample.H == nil:
			{
Repository: prometheus/prometheus
File: promql/functions.go (Go)

Code before:
}

// mergeSeries merges the floats and the histograms in a series
// into a slice of Sample sorted by their timestamp.
// it assumes that the floats and histograms are sorted by their timestamp.
func mergeSeries(series Series) []*Sample {
	floats := series.Floats
	histograms := series.Histograms
	samples := make([]*Sample, 0, len(floats)+len(histograms))

	// i for iterating through floats and j for iterating through histograms
	i, j := 0, 0
	for i < len(floats) && j < len(histograms) {
		if floats[i].T < histograms[j].T {
			samples = append(samples, &Sample{
				F: floats[i].F,
			})
			i++
		} else {
			samples = append(samples, &Sample{
				H: histograms[j].H,
			})
			j++
		}
	}
	// Append the remaining samples
	for i < len(floats) {
		samples = append(samples, &Sample{
			F: floats[i].F,
		})
		i++
	}
	for j < len(histograms) {
		samples = append(samples, &Sample{
			H: histograms[j].H,
		})
		j++
	}

	return samples
}

// === changes(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
func funcChanges(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
	samples := mergeSeries(vals[0].(Matrix)[0])
	changes := 0
	if len(samples) == 0 {
		return enh.Out, nil
	}

	prevSample := samples[0]

Review comment (suggestion):
```suggestion
	// Append the remaining samples.
```

Code after:
}

// === changes(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
func funcChanges(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
	floats := vals[0].(Matrix)[0].Floats
	histograms := vals[0].(Matrix)[0].Histograms
	changes := 0
	if len(floats) == 0 && len(histograms) == 0 {
		return enh.Out, nil
	}

	var prevSample, curSample Sample
	for iFloat, iHistogram := 0, 0; iFloat < len(floats) || iHistogram < len(histograms); {
		switch {
		// Process a float sample if no histogram sample remains or its timestamp is earlier.
		// Process a histogram sample if no float sample remains or its timestamp is earlier.
		case iHistogram >= len(histograms) || iFloat < len(floats) && floats[iFloat].T < histograms[iHistogram].T:
			{
				curSample.F = floats[iFloat].F
				curSample.H = nil
				iFloat++
			}
		case iFloat >= len(floats) || iHistogram < len(histograms) && floats[iFloat].T > histograms[iHistogram].T:
			{
				curSample.H = histograms[iHistogram].H
				iHistogram++
			}
		}
		// Skip the comparison for the first sample, just initialize prevSample.
		if iFloat+iHistogram == 1 {
			prevSample = curSample
			continue
		}
		switch {
		case prevSample.H == nil && curSample.H == nil:
			{
				if curSample.F != prevSample.F && !(math.IsNaN(curSample.F) && math.IsNaN(prevSample.F)) {
					changes++
				}
			}
		case prevSample.H != nil && curSample.H == nil, prevSample.H == nil && curSample.H != nil:
			{
				changes++
			}
		case prevSample.H != nil && curSample.H != nil:
			{
				if !curSample.H.Equals(prevSample.H) {
					changes++
				}
			}
		}
Repository: prometheus/prometheus
File: promql/engine_test.go (Go)

Code before:
func NewFakeQueryLogger() *FakeQueryLogger {
	return &FakeQueryLogger{
		closed: false,
		logs:   make([]interface{}, 0),
		attrs:  make([]any, 0),
	}
}

func (f *FakeQueryLogger) Close() error {
	f.closed = true
	return nil
}

func (f *FakeQueryLogger) Enabled(ctx context.Context, level slog.Level) bool {
	return true
}

func (f *FakeQueryLogger) Handle(ctx context.Context, r slog.Record) error {
	// Test usage only really cares about existence of keyvals passed in
	// via args, just append in the log message before handling the
	// provided args and any embedded kvs added via `.With()` on f.attrs.
	log := append([]any{r.Message}, f.attrs...)
	f.attrs = f.attrs[:0]

	r.Attrs(func(a slog.Attr) bool {
		log = append(log, a.Key, a.Value.Any())
		return true
	})

	f.logs = append(f.logs, log...)
	return nil
}

func (f *FakeQueryLogger) WithAttrs(attrs []slog.Attr) slog.Handler {
	attrPairs := []any{}
	for _, a := range attrs {
		attrPairs = append(attrPairs, a.Key, a.Value.Any())
	}

	return &FakeQueryLogger{
		closed: f.closed,
		logs:   f.logs,
		attrs:  append(f.attrs, attrPairs...),
	}
}

func (f *FakeQueryLogger) WithGroup(name string) slog.Handler {
	// We don't need to support groups for this fake handler in testing.
	return f
}


Review comment (suggestion):
I think this could be simplified by holding `[]slog.Attr` and modifying the one test that looks at them.

Code after:
	}
	return lines
}

func TestQueryLogger_basic(t *testing.T) {
	opts := promql.EngineOpts{
		Logger:     nil,
		Reg:        nil,
		MaxSamples: 10,
		Timeout:    10 * time.Second,
	}
	engine := promqltest.NewTestEngineWithOpts(t, opts)

	queryExec := func() {
		ctx, cancelCtx := context.WithCancel(context.Background())
		defer cancelCtx()
		query := engine.NewTestQuery(func(ctx context.Context) error {
			return contextDone(ctx, "test statement execution")
		})
		res := query.Exec(ctx)
		require.NoError(t, res.Err)
	}

	// promql.Query works without query log initialized.
	queryExec()

	tmpDir := t.TempDir()
	ql1File := filepath.Join(tmpDir, "query1.log")
	f1, err := logging.NewJSONFileLogger(ql1File)
	require.NoError(t, err)

	engine.SetQueryLogger(f1)
	queryExec()
	logLines := getLogLines(t, ql1File)
	require.Contains(t, logLines[0], "params", map[string]interface{}{"query": "test statement"})
	require.Len(t, logLines, 1)

	l := len(logLines)
	queryExec()
	logLines = getLogLines(t, ql1File)
	l2 := len(logLines)
	require.Equal(t, l2, 2*l)

	// Test that we close the query logger when unsetting it. The following
	// attempt to close the file should error.
	engine.SetQueryLogger(nil)
	err = f1.Close()
	require.ErrorContains(t, err, "file already closed", "expected f1 to be closed, got open")
	queryExec()

	// Test that we close the query logger when swapping.
Repository: prometheus/prometheus
File: promql/parser/printer.go (Go)

Code before:
	return aggrString
}

func (node *AggregateExpr) ShortString() string {
	aggrString := node.getAggOpStr()
	return aggrString
}

func (node *AggregateExpr) getAggOpStr() string {
	aggrString := node.Op.String()

	switch {
	case node.Without:
		aggrString += fmt.Sprintf(" without (%s) ", joinLabels(node.Grouping))
	case len(node.Grouping) > 0:
		aggrString += fmt.Sprintf(" by (%s) ", joinLabels(node.Grouping))
	}

	return aggrString
}

func joinLabels(ss []string) string {
	labelList := ""
	for i, s := range ss {
		if i > 0 {
			labelList += ", "
		}
		// If the label is already quoted, don't quote it again.
		if s[0] != '"' && s[0] != '\'' && s[0] != '`' && !model.IsValidLegacyMetricName(string(model.LabelValue(s))) {
			labelList += fmt.Sprintf("\"%s\"", s)
		} else {
			labelList += s
		}
	}
	return labelList
}

func (node *BinaryExpr) returnBool() string {
	if node.ReturnBool {
		return " bool"
	}
	return ""
}

func (node *BinaryExpr) String() string {
	matching := node.getMatchingStr()
	return fmt.Sprintf("%s %s%s%s %s", node.LHS, node.Op, node.returnBool(), matching, node.RHS)
}

func (node *BinaryExpr) ShortString() string {
	return fmt.Sprintf("%s%s%s", node.Op, node.returnBool(), node.getMatchingStr())

Review comment (security):
If this is called in performance-sensitive circumstances then use a buffer or builder. 

Code after:
	aggrString += fmt.Sprintf("%s)", node.Expr)

	return aggrString
}

func (node *AggregateExpr) ShortString() string {
	aggrString := node.getAggOpStr()
	return aggrString
}

func (node *AggregateExpr) getAggOpStr() string {
	aggrString := node.Op.String()

	switch {
	case node.Without:
		aggrString += fmt.Sprintf(" without (%s) ", joinLabels(node.Grouping))
	case len(node.Grouping) > 0:
		aggrString += fmt.Sprintf(" by (%s) ", joinLabels(node.Grouping))
	}

	return aggrString
}

func joinLabels(ss []string) string {
	var bytea [1024]byte // On stack to avoid memory allocation while building the output.
	b := bytes.NewBuffer(bytea[:0])

	for i, s := range ss {
		if i > 0 {
			b.WriteString(", ")
		}
		if !model.IsValidLegacyMetricName(string(model.LabelValue(s))) {
			b.Write(strconv.AppendQuote(b.AvailableBuffer(), s))
		} else {
			b.WriteString(s)
		}
	}
	return b.String()
}

func (node *BinaryExpr) returnBool() string {
	if node.ReturnBool {
		return " bool"
	}
	return ""
}

func (node *BinaryExpr) String() string {
	matching := node.getMatchingStr()
	return fmt.Sprintf("%s %s%s%s %s", node.LHS, node.Op, node.returnBool(), matching, node.RHS)
}
Repository: prometheus/prometheus
File: model/labels/labels_common.go (Go)

Code before:
	InstanceName = "instance"

	labelSep = '\xfe' // Used at beginning of `Bytes` return.
	sep      = '\xff' // Used between labels in `Bytes` and `Hash`.
)

var seps = []byte{sep} // Used with Hash, which has no WriteByte method.

// Label is a key/value pair of strings.
type Label struct {
	Name, Value string
}

func (ls Labels) String() string {
	var bytea [1024]byte // On stack to avoid memory allocation while building the output.
	b := bytes.NewBuffer(bytea[:0])

	b.WriteByte('{')
	i := 0
	ls.Range(func(l Label) {
		if i > 0 {
			b.WriteByte(',')
			b.WriteByte(' ')
		}
		if !model.LabelName(l.Name).IsValidLegacy() {
			b.WriteString(strconv.Quote(l.Name))
		} else {
			b.WriteString(l.Name)
		}
		b.WriteByte('=')
		b.Write(strconv.AppendQuote(b.AvailableBuffer(), l.Value))
		i++
	})
	b.WriteByte('}')
	return b.String()
}

// MarshalJSON implements json.Marshaler.
func (ls Labels) MarshalJSON() ([]byte, error) {
	return json.Marshal(ls.Map())
}

// UnmarshalJSON implements json.Unmarshaler.
func (ls *Labels) UnmarshalJSON(b []byte) error {
	var m map[string]string

	if err := json.Unmarshal(b, &m); err != nil {
		return err
	}

	*ls = FromMap(m)

Review comment (suggestion):
```suggestion
			b.Write(strconv.AppendQuote(b.AvailableBuffer(), l.Name))
```
Saves an allocation.  (This pattern is also on the `Value` 5 lines below)

Code after:
	InstanceName = "instance"

	labelSep = '\xfe' // Used at beginning of `Bytes` return.
	sep      = '\xff' // Used between labels in `Bytes` and `Hash`.
)

var seps = []byte{sep} // Used with Hash, which has no WriteByte method.

// Label is a key/value pair of strings.
type Label struct {
	Name, Value string
}

func (ls Labels) String() string {
	var bytea [1024]byte // On stack to avoid memory allocation while building the output.
	b := bytes.NewBuffer(bytea[:0])

	b.WriteByte('{')
	i := 0
	ls.Range(func(l Label) {
		if i > 0 {
			b.WriteByte(',')
			b.WriteByte(' ')
		}
		if !model.LabelName(l.Name).IsValidLegacy() {
			b.Write(strconv.AppendQuote(b.AvailableBuffer(), l.Name))
		} else {
			b.WriteString(l.Name)
		}
		b.WriteByte('=')
		b.Write(strconv.AppendQuote(b.AvailableBuffer(), l.Value))
		i++
	})
	b.WriteByte('}')
	return b.String()
}

// MarshalJSON implements json.Marshaler.
func (ls Labels) MarshalJSON() ([]byte, error) {
	return json.Marshal(ls.Map())
}

// UnmarshalJSON implements json.Unmarshaler.
func (ls *Labels) UnmarshalJSON(b []byte) error {
	var m map[string]string

	if err := json.Unmarshal(b, &m); err != nil {
		return err
	}

	*ls = FromMap(m)
Repository: prometheus/prometheus
File: promql/parser/printer.go (Go)

Code before:
func (node *AggregateExpr) ShortString() string {
	aggrString := node.getAggOpStr()
	return aggrString
}

func (node *AggregateExpr) getAggOpStr() string {
	aggrString := node.Op.String()

	switch {
	case node.Without:
		aggrString += fmt.Sprintf(" without (%s) ", joinLabels(node.Grouping))
	case len(node.Grouping) > 0:
		aggrString += fmt.Sprintf(" by (%s) ", joinLabels(node.Grouping))
	}

	return aggrString
}

func joinLabels(ss []string) string {
	b := strings.Builder{}
	for i, s := range ss {
		if i > 0 {
			b.WriteString(", ")
		}
		// If the label is already quoted, don't quote it again.
		if s[0] != '"' && s[0] != '\'' && s[0] != '`' && !model.IsValidLegacyMetricName(string(model.LabelValue(s))) {
			b.WriteString(strconv.Quote(s))
		} else {
			b.WriteString(s)
		}
	}
	return b.String()
}

func (node *BinaryExpr) returnBool() string {
	if node.ReturnBool {
		return " bool"
	}
	return ""
}

func (node *BinaryExpr) String() string {
	matching := node.getMatchingStr()
	return fmt.Sprintf("%s %s%s%s %s", node.LHS, node.Op, node.returnBool(), matching, node.RHS)
}

func (node *BinaryExpr) ShortString() string {
	return fmt.Sprintf("%s%s%s", node.Op, node.returnBool(), node.getMatchingStr())
}

func (node *BinaryExpr) getMatchingStr() string {

Review comment (suggestion):
```suggestion
			b.Write(strconv.AppendQuote(b.AvailableBuffer(), s))
```
Saves an allocation.

Code after:

func (node *AggregateExpr) ShortString() string {
	aggrString := node.getAggOpStr()
	return aggrString
}

func (node *AggregateExpr) getAggOpStr() string {
	aggrString := node.Op.String()

	switch {
	case node.Without:
		aggrString += fmt.Sprintf(" without (%s) ", joinLabels(node.Grouping))
	case len(node.Grouping) > 0:
		aggrString += fmt.Sprintf(" by (%s) ", joinLabels(node.Grouping))
	}

	return aggrString
}

func joinLabels(ss []string) string {
	var bytea [1024]byte // On stack to avoid memory allocation while building the output.
	b := bytes.NewBuffer(bytea[:0])

	for i, s := range ss {
		if i > 0 {
			b.WriteString(", ")
		}
		if !model.IsValidLegacyMetricName(string(model.LabelValue(s))) {
			b.Write(strconv.AppendQuote(b.AvailableBuffer(), s))
		} else {
			b.WriteString(s)
		}
	}
	return b.String()
}

func (node *BinaryExpr) returnBool() string {
	if node.ReturnBool {
		return " bool"
	}
	return ""
}

func (node *BinaryExpr) String() string {
	matching := node.getMatchingStr()
	return fmt.Sprintf("%s %s%s%s %s", node.LHS, node.Op, node.returnBool(), matching, node.RHS)
}

func (node *BinaryExpr) ShortString() string {
	return fmt.Sprintf("%s%s%s", node.Op, node.returnBool(), node.getMatchingStr())
}
Repository: prometheus/prometheus
File: web/api/v1/api.go (Go)

Code before:
	opts, err := extractQueryOpts(r)
	if err != nil {
		return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil}
	}
	qry, err := api.QueryEngine.NewInstantQuery(ctx, api.Queryable, opts, r.FormValue("query"), ts)
	if err != nil {
		return invalidParamError(err, "query")
	}

	// From now on, we must only return with a finalizer in the result (to
	// be called by the caller) or call qry.Close ourselves (which is
	// required in the case of a panic).
	defer func() {
		if result.finalizer == nil {
			qry.Close()
		}
	}()

	ctx = httputil.ContextFromRequest(ctx, r)

	res := qry.Exec(ctx)
	if res.Err != nil {
		return apiFuncResult{nil, returnAPIError(res.Err), res.Warnings, qry.Close}
	}

	var warnings annotations.Annotations
	if limit > 0 {
		res = truncateResults(res, limit)
		warnings = warnings.Add(errors.New("results truncated due to limit"))
	}
	// Optional stats field in response if parameter "stats" is not empty.
	sr := api.statsRenderer
	if sr == nil {
		sr = DefaultStatsRenderer
	}
	qs := sr(ctx, qry.Stats(), r.FormValue("stats"))

	return apiFuncResult{&QueryData{
		ResultType: res.Value.Type(),
		Result:     res.Value,
		Stats:      qs,
	}, nil, warnings, qry.Close}
}

func (api *API) formatQuery(r *http.Request) (result apiFuncResult) {
	expr, err := parser.ParseExpr(r.FormValue("query"))
	if err != nil {
		return invalidParamError(err, "query")
	}

	return apiFuncResult{expr.Pretty(0), nil, nil, nil}

Review comment (suggestion):
```suggestion
	warnings := res.Warnings
```

We should not disregard the warnings coming from the query.

Code after:
	opts, err := extractQueryOpts(r)
	if err != nil {
		return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil}
	}
	qry, err := api.QueryEngine.NewInstantQuery(ctx, api.Queryable, opts, r.FormValue("query"), ts)
	if err != nil {
		return invalidParamError(err, "query")
	}

	// From now on, we must only return with a finalizer in the result (to
	// be called by the caller) or call qry.Close ourselves (which is
	// required in the case of a panic).
	defer func() {
		if result.finalizer == nil {
			qry.Close()
		}
	}()

	ctx = httputil.ContextFromRequest(ctx, r)

	res := qry.Exec(ctx)
	if res.Err != nil {
		return apiFuncResult{nil, returnAPIError(res.Err), res.Warnings, qry.Close}
	}

	warnings := res.Warnings
	if limit > 0 {
		var isTruncated bool

		res, isTruncated = truncateResults(res, limit)
		if isTruncated {
			warnings = warnings.Add(errors.New("results truncated due to limit"))
		}
	}
	// Optional stats field in response if parameter "stats" is not empty.
	sr := api.statsRenderer
	if sr == nil {
		sr = DefaultStatsRenderer
	}
	qs := sr(ctx, qry.Stats(), r.FormValue("stats"))

	return apiFuncResult{&QueryData{
		ResultType: res.Value.Type(),
		Result:     res.Value,
		Stats:      qs,
	}, nil, warnings, qry.Close}
}

func (api *API) formatQuery(r *http.Request) (result apiFuncResult) {
	expr, err := parser.ParseExpr(r.FormValue("query"))
	if err != nil {
Repository: prometheus/prometheus
File: web/api/v1/api.go (Go)

Code before:
	}
	qry, err := api.QueryEngine.NewInstantQuery(ctx, api.Queryable, opts, r.FormValue("query"), ts)
	if err != nil {
		return invalidParamError(err, "query")
	}

	// From now on, we must only return with a finalizer in the result (to
	// be called by the caller) or call qry.Close ourselves (which is
	// required in the case of a panic).
	defer func() {
		if result.finalizer == nil {
			qry.Close()
		}
	}()

	ctx = httputil.ContextFromRequest(ctx, r)

	res := qry.Exec(ctx)
	if res.Err != nil {
		return apiFuncResult{nil, returnAPIError(res.Err), res.Warnings, qry.Close}
	}

	var warnings annotations.Annotations
	if limit > 0 {
		res = truncateResults(res, limit)
		warnings = warnings.Add(errors.New("results truncated due to limit"))
	}
	// Optional stats field in response if parameter "stats" is not empty.
	sr := api.statsRenderer
	if sr == nil {
		sr = DefaultStatsRenderer
	}
	qs := sr(ctx, qry.Stats(), r.FormValue("stats"))

	return apiFuncResult{&QueryData{
		ResultType: res.Value.Type(),
		Result:     res.Value,
		Stats:      qs,
	}, nil, warnings, qry.Close}
}

func (api *API) formatQuery(r *http.Request) (result apiFuncResult) {
	expr, err := parser.ParseExpr(r.FormValue("query"))
	if err != nil {
		return invalidParamError(err, "query")
	}

	return apiFuncResult{expr.Pretty(0), nil, nil, nil}
}

func (api *API) parseQuery(r *http.Request) apiFuncResult {

Review comment (suggestion):
You should only add the warning if the results were actually truncated.

`truncateResults` could return an additional `bool` telling the caller if a truncation has happened.

Code after:
	}
	qry, err := api.QueryEngine.NewInstantQuery(ctx, api.Queryable, opts, r.FormValue("query"), ts)
	if err != nil {
		return invalidParamError(err, "query")
	}

	// From now on, we must only return with a finalizer in the result (to
	// be called by the caller) or call qry.Close ourselves (which is
	// required in the case of a panic).
	defer func() {
		if result.finalizer == nil {
			qry.Close()
		}
	}()

	ctx = httputil.ContextFromRequest(ctx, r)

	res := qry.Exec(ctx)
	if res.Err != nil {
		return apiFuncResult{nil, returnAPIError(res.Err), res.Warnings, qry.Close}
	}

	warnings := res.Warnings
	if limit > 0 {
		var isTruncated bool

		res, isTruncated = truncateResults(res, limit)
		if isTruncated {
			warnings = warnings.Add(errors.New("results truncated due to limit"))
		}
	}
	// Optional stats field in response if parameter "stats" is not empty.
	sr := api.statsRenderer
	if sr == nil {
		sr = DefaultStatsRenderer
	}
	qs := sr(ctx, qry.Stats(), r.FormValue("stats"))

	return apiFuncResult{&QueryData{
		ResultType: res.Value.Type(),
		Result:     res.Value,
		Stats:      qs,
	}, nil, warnings, qry.Close}
}

func (api *API) formatQuery(r *http.Request) (result apiFuncResult) {
	expr, err := parser.ParseExpr(r.FormValue("query"))
	if err != nil {
		return invalidParamError(err, "query")
	}