SQL Expressions: Add sql expression specific timeout and output limit (#104834)

Adds settings for SQL expressions:
 sql_expression_cell_output_limit

Set the maximum number of cells that can be returned from a SQL expression. Default is 100000.

sql_expression_timeout

The duration a SQL expression will run before being cancelled. The default is 10s.
This commit is contained in:
Kyle Brandt
2025-05-13 15:22:20 -04:00
committed by GitHub
parent 02d977e1af
commit 5e056c2a3f
12 changed files with 183 additions and 22 deletions
+25 -1
View File
@@ -16,8 +16,9 @@ import (
)
// TODO: Should this accept a row limit and converters, like sqlutil.FrameFromRows?
func convertToDataFrame(ctx *mysql.Context, iter mysql.RowIter, schema mysql.Schema) (*data.Frame, error) {
func convertToDataFrame(ctx *mysql.Context, iter mysql.RowIter, schema mysql.Schema, maxOutputCells int64) (*data.Frame, error) {
f := &data.Frame{}
// Create fields based on the schema
for _, col := range schema {
fT, err := MySQLColToFieldType(col)
@@ -29,8 +30,17 @@ func convertToDataFrame(ctx *mysql.Context, iter mysql.RowIter, schema mysql.Sch
f.Fields = append(f.Fields, field)
}
cellCount := int64(0)
// Iterate through the rows and append data to fields
for {
// Check for context cancellation or timeout
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
row, err := iter.Next(ctx)
if errors.Is(err, io.EOF) {
break
@@ -39,6 +49,20 @@ func convertToDataFrame(ctx *mysql.Context, iter mysql.RowIter, schema mysql.Sch
return nil, fmt.Errorf("error reading row: %v", err)
}
// We check the cell count here to avoid appending an incomplete row, so the
// the number returned may be less than the maxOutputCells.
// If the maxOutputCells is 0, we don't check the cell count.
if maxOutputCells > 0 {
cellCount += int64(len(row))
if cellCount > maxOutputCells {
f.AppendNotices(data.Notice{
Severity: data.NoticeSeverityWarning,
Text: fmt.Sprintf("Query exceeded max output cells (%d). Only %d cells returned.", maxOutputCells, cellCount-int64(len(row))),
})
return f, nil
}
}
for i, val := range row {
// Run val through mysql.Type.Convert to normalize underlying value
// of the interface