From 9a5e10e6fb8ba14362c53fe1cb69b1a804d5ff4d Mon Sep 17 00:00:00 2001 From: Marc Bachmann Date: Wed, 9 Jun 2021 15:14:28 +0200 Subject: [PATCH] Annotations: Fix sql annotation parsing for empty responses (#35367) * fix sql annotation parsing for empty responses * fix backend when no data returned * add back frontend changes Co-authored-by: Ying WANG --- pkg/tsdb/sqleng/sql_engine.go | 41 ++++++++++--------- .../datasource/mssql/response_parser.ts | 3 ++ .../datasource/mysql/response_parser.ts | 3 ++ .../datasource/postgres/response_parser.ts | 3 ++ 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/pkg/tsdb/sqleng/sql_engine.go b/pkg/tsdb/sqleng/sql_engine.go index ac821a1609f..5f8112590c7 100644 --- a/pkg/tsdb/sqleng/sql_engine.go +++ b/pkg/tsdb/sqleng/sql_engine.go @@ -206,38 +206,37 @@ func (e *dataPlugin) executeQuery(query plugins.DataSubQuery, wg *sync.WaitGroup timeRange = *queryContext.TimeRange } + errAppendDebug := func(frameErr string, err error, query string) { + var emptyFrame data.Frame + emptyFrame.SetMeta(&data.FrameMeta{ + ExecutedQueryString: query, + }) + queryResult.Error = fmt.Errorf("%s: %w", frameErr, err) + queryResult.Dataframes = plugins.NewDecodedDataFrames(data.Frames{&emptyFrame}) + ch <- queryResult + } + // global substitutions interpolatedQuery, err := Interpolate(query, timeRange, rawSQL) if err != nil { - queryResult.Error = err - ch <- queryResult + errAppendDebug("interpolation failed", e.transformQueryError(err), interpolatedQuery) return } // data source specific substitutions interpolatedQuery, err = e.macroEngine.Interpolate(query, timeRange, interpolatedQuery) if err != nil { - queryResult.Error = err - ch <- queryResult + errAppendDebug("interpolation failed", e.transformQueryError(err), interpolatedQuery) return } - errAppendDebug := func(frameErr string, err error) { - var emptyFrame data.Frame - emptyFrame.SetMeta(&data.FrameMeta{ - ExecutedQueryString: interpolatedQuery, - }) - queryResult.Error = fmt.Errorf("%s: %w", frameErr, err) - queryResult.Dataframes = plugins.NewDecodedDataFrames(data.Frames{&emptyFrame}) - ch <- queryResult - } session := e.engine.NewSession() defer session.Close() db := session.DB() rows, err := db.Query(interpolatedQuery) if err != nil { - errAppendDebug("db query error", e.transformQueryError(err)) + errAppendDebug("db query error", e.transformQueryError(err), interpolatedQuery) return } defer func() { @@ -248,7 +247,7 @@ func (e *dataPlugin) executeQuery(query plugins.DataSubQuery, wg *sync.WaitGroup qm, err := e.newProcessCfg(query, queryContext, rows, interpolatedQuery) if err != nil { - errAppendDebug("failed to get configurations", err) + errAppendDebug("failed to get configurations", err, interpolatedQuery) return } @@ -256,7 +255,7 @@ func (e *dataPlugin) executeQuery(query plugins.DataSubQuery, wg *sync.WaitGroup stringConverters := e.queryResultTransformer.GetConverterList() frame, err := sqlutil.FrameFromRows(rows.Rows, rowLimit, sqlutil.ToConverters(stringConverters...)...) if err != nil { - errAppendDebug("convert frame from rows error", err) + errAppendDebug("convert frame from rows error", err, interpolatedQuery) return } @@ -266,12 +265,14 @@ func (e *dataPlugin) executeQuery(query plugins.DataSubQuery, wg *sync.WaitGroup // If no rows were returned, no point checking anything else. if frame.Rows() == 0 { + queryResult.Dataframes = plugins.NewDecodedDataFrames(data.Frames{frame}) + ch <- queryResult return } if qm.timeIndex != -1 { if err := convertSQLTimeColumnToEpochMS(frame, qm.timeIndex); err != nil { - errAppendDebug("db convert time column failed", err) + errAppendDebug("db convert time column failed", err, interpolatedQuery) return } } @@ -279,7 +280,7 @@ func (e *dataPlugin) executeQuery(query plugins.DataSubQuery, wg *sync.WaitGroup if qm.Format == dataQueryFormatSeries { // time series has to have time column if qm.timeIndex == -1 { - errAppendDebug("db has no time column", errors.New("no time column found")) + errAppendDebug("db has no time column", errors.New("no time column found"), interpolatedQuery) return } for i := range qm.columnNames { @@ -289,7 +290,7 @@ func (e *dataPlugin) executeQuery(query plugins.DataSubQuery, wg *sync.WaitGroup var err error if frame, err = convertSQLValueColumnToFloat(frame, i); err != nil { - errAppendDebug("convert value to float failed", err) + errAppendDebug("convert value to float failed", err, interpolatedQuery) return } } @@ -299,7 +300,7 @@ func (e *dataPlugin) executeQuery(query plugins.DataSubQuery, wg *sync.WaitGroup var err error frame, err = data.LongToWide(frame, qm.FillMissing) if err != nil { - errAppendDebug("failed to convert long to wide series when converting from dataframe", err) + errAppendDebug("failed to convert long to wide series when converting from dataframe", err, interpolatedQuery) return } } diff --git a/public/app/plugins/datasource/mssql/response_parser.ts b/public/app/plugins/datasource/mssql/response_parser.ts index e0580789d5e..05a853170a6 100644 --- a/public/app/plugins/datasource/mssql/response_parser.ts +++ b/public/app/plugins/datasource/mssql/response_parser.ts @@ -37,6 +37,9 @@ export default class ResponseParser { async transformAnnotationResponse(options: any, data: BackendDataSourceResponse): Promise { const frames = toDataQueryResponse({ data: data }).data as DataFrame[]; + if (!frames || !frames.length) { + return []; + } const frame = frames[0]; const timeField = frame.fields.find((f) => f.name === 'time'); diff --git a/public/app/plugins/datasource/mysql/response_parser.ts b/public/app/plugins/datasource/mysql/response_parser.ts index ac946ad12dc..c4367a9c402 100644 --- a/public/app/plugins/datasource/mysql/response_parser.ts +++ b/public/app/plugins/datasource/mysql/response_parser.ts @@ -37,6 +37,9 @@ export default class ResponseParser { async transformAnnotationResponse(options: any, data: BackendDataSourceResponse): Promise { const frames = toDataQueryResponse({ data: data }).data as DataFrame[]; + if (!frames || !frames.length) { + return []; + } const frame = frames[0]; const timeField = frame.fields.find((f) => f.name === 'time' || f.name === 'time_sec'); diff --git a/public/app/plugins/datasource/postgres/response_parser.ts b/public/app/plugins/datasource/postgres/response_parser.ts index f0e40ef01ad..83ecc223984 100644 --- a/public/app/plugins/datasource/postgres/response_parser.ts +++ b/public/app/plugins/datasource/postgres/response_parser.ts @@ -37,6 +37,9 @@ export default class ResponseParser { async transformAnnotationResponse(options: any, data: BackendDataSourceResponse): Promise { const frames = toDataQueryResponse({ data: data }).data as DataFrame[]; + if (!frames || !frames.length) { + return []; + } const frame = frames[0]; const timeField = frame.fields.find((f) => f.name === 'time');