From 9e33be2619cd0d7ad5a4323455bf5c3308908f23 Mon Sep 17 00:00:00 2001 From: davidby-influx <72418212+davidby-influx@users.noreply.github.com> Date: Wed, 30 Dec 2020 18:22:43 -0800 Subject: [PATCH] fix(error): SELECT INTO doesn't return error with unsupported value (#20429) When a SELECT INTO query generates an illegal value that cannot be inserted, like +/- Inf, it should return an error, rather than failing silently. This adds a boolean parameter to the [data] section of influxdb.conf: * strict-error-handling When false, the default, the old behavior is preserved. When true, unsupported values will return an error from SELECT INTO queries Fixes https://github.com/influxdata/influxdb/issues/20426 --- cmd/influxd/run/server.go | 11 ++++++----- coordinator/statement_executor.go | 20 +++++++++++++------- etc/config.sample.toml | 4 ++++ tsdb/config.go | 7 ++++++- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/cmd/influxd/run/server.go b/cmd/influxd/run/server.go index 915f5ebf485..5738f5fbe9f 100644 --- a/cmd/influxd/run/server.go +++ b/cmd/influxd/run/server.go @@ -215,11 +215,12 @@ func NewServer(c *Config, buildInfo *BuildInfo) (*Server, error) { MetaClient: s.MetaClient, TSDBStore: coordinator.LocalTSDBStore{Store: s.TSDBStore}, }, - Monitor: s.Monitor, - PointsWriter: s.PointsWriter, - MaxSelectPointN: c.Coordinator.MaxSelectPointN, - MaxSelectSeriesN: c.Coordinator.MaxSelectSeriesN, - MaxSelectBucketsN: c.Coordinator.MaxSelectBucketsN, + StrictErrorHandling: s.TSDBStore.EngineOptions.Config.StrictErrorHandling, + Monitor: s.Monitor, + PointsWriter: s.PointsWriter, + MaxSelectPointN: c.Coordinator.MaxSelectPointN, + MaxSelectSeriesN: c.Coordinator.MaxSelectSeriesN, + MaxSelectBucketsN: c.Coordinator.MaxSelectBucketsN, } s.QueryExecutor.TaskManager.QueryTimeout = time.Duration(c.Coordinator.QueryTimeout) s.QueryExecutor.TaskManager.LogQueriesAfter = time.Duration(c.Coordinator.LogQueriesAfter) diff --git a/coordinator/statement_executor.go b/coordinator/statement_executor.go index df19f345fcf..150160b9da1 100644 --- a/coordinator/statement_executor.go +++ b/coordinator/statement_executor.go @@ -51,6 +51,9 @@ type StatementExecutor struct { WritePointsInto(*IntoWriteRequest) error } + // Disallow INF values in SELECT INTO and other previously ignored errors + StrictErrorHandling bool + // Select statement limits MaxSelectPointN int MaxSelectSeriesN int @@ -573,7 +576,7 @@ func (e *StatementExecutor) executeSelectStatement(stmt *influxql.SelectStatemen // Write points back into system for INTO statements. if stmt.Target != nil { - n, err := e.writeInto(pointsWriter, stmt, row) + n, err := e.writeInto(pointsWriter, stmt, row, e.StrictErrorHandling) if err != nil { return err } @@ -1188,7 +1191,7 @@ func (w *BufferedPointsWriter) Len() int { return len(w.buf) } // Cap returns the capacity (in points) of the buffer. func (w *BufferedPointsWriter) Cap() int { return cap(w.buf) } -func (e *StatementExecutor) writeInto(w pointsWriter, stmt *influxql.SelectStatement, row *models.Row) (n int64, err error) { +func (e *StatementExecutor) writeInto(w pointsWriter, stmt *influxql.SelectStatement, row *models.Row, strictErrorHandling bool) (n int64, err error) { if stmt.Target.Measurement.Database == "" { return 0, errNoDatabaseInTarget } @@ -1205,7 +1208,7 @@ func (e *StatementExecutor) writeInto(w pointsWriter, stmt *influxql.SelectState name = row.Name } - points, err := convertRowToPoints(name, row) + points, err := convertRowToPoints(name, row, strictErrorHandling) if err != nil { return 0, err } @@ -1224,7 +1227,7 @@ func (e *StatementExecutor) writeInto(w pointsWriter, stmt *influxql.SelectState var errNoDatabaseInTarget = errors.New("no database in target") // convertRowToPoints will convert a query result Row into Points that can be written back in. -func convertRowToPoints(measurementName string, row *models.Row) ([]models.Point, error) { +func convertRowToPoints(measurementName string, row *models.Row, strictErrorHandling bool) ([]models.Point, error) { // figure out which parts of the result are the time and which are the fields timeIndex := -1 fieldIndexes := make(map[string]int) @@ -1256,13 +1259,16 @@ func convertRowToPoints(measurementName string, row *models.Row) ([]models.Point p, err := models.NewPoint(measurementName, models.NewTags(row.Tags), vals, v[timeIndex].(time.Time)) if err != nil { - // Drop points that can't be stored - continue + if !strictErrorHandling { + // Drop points that can't be stored + continue + } else { + return nil, err + } } points = append(points, p) } - return points, nil } diff --git a/etc/config.sample.toml b/etc/config.sample.toml index 89eac990d5d..f9b17960621 100644 --- a/etc/config.sample.toml +++ b/etc/config.sample.toml @@ -67,6 +67,10 @@ # log any sensitive data contained within a query. # query-log-enabled = true + # Provides more error checking. For example, SELECT INTO will err out inserting an +/-Inf value + # rather than silently failing. + # strict-error-handling = false + # Validates incoming writes to ensure keys only have valid unicode characters. # This setting will incur a small overhead because every key must be checked. # validate-keys = false diff --git a/tsdb/config.go b/tsdb/config.go index bcc4fd4e719..025a84713ce 100644 --- a/tsdb/config.go +++ b/tsdb/config.go @@ -93,6 +93,9 @@ type Config struct { // Enables unicode validation on series keys on write. ValidateKeys bool `toml:"validate-keys"` + // Enables strict error handling. For example, forces SELECT INTO to err out on INF values. + StrictErrorHandling bool `toml:"strict-error-handling"` + // Query logging QueryLogEnabled bool `toml:"query-log-enabled"` @@ -155,7 +158,8 @@ func NewConfig() Config { Engine: DefaultEngine, Index: DefaultIndex, - QueryLogEnabled: true, + StrictErrorHandling: false, + QueryLogEnabled: true, CacheMaxMemorySize: toml.Size(DefaultCacheMaxMemorySize), CacheSnapshotMemorySize: toml.Size(DefaultCacheSnapshotMemorySize), @@ -229,6 +233,7 @@ func (c Config) Diagnostics() (*diagnostics.Diagnostics, error) { "dir": c.Dir, "wal-dir": c.WALDir, "wal-fsync-delay": c.WALFsyncDelay, + "strict-error-handling": c.StrictErrorHandling, "cache-max-memory-size": c.CacheMaxMemorySize, "cache-snapshot-memory-size": c.CacheSnapshotMemorySize, "cache-snapshot-write-cold-duration": c.CacheSnapshotWriteColdDuration,