Skip to content

Fix InfluxDB dropping columns when a query returns several series - #7805

Open
MaxFreedomPollard wants to merge 2 commits into
getredash:masterfrom
MaxFreedomPollard:fix-influxdb-multi-series-columns
Open

MaxFreedomPollard wants to merge 2 commits into
getredash:masterfrom
MaxFreedomPollard:fix-influxdb-multi-series-columns

Conversation

@MaxFreedomPollard

@MaxFreedomPollard MaxFreedomPollard commented Sep 7, 2026

Copy link
Copy Markdown

What type of PR is this?

  • Bug Fix

Description

_transform_result in redash/query_runner/influx_db.py walks every series first and collects column_names, the union of the column names and tag keys across all of them. It then discards that union and builds the result columns from result_rows[0].keys() whenever there is at least one row.

Any InfluxDB query that returns several series with different column sets hits this. SELECT * FROM /^(cpu|mem)$/ returns one series per measurement, and two statements separated by a semicolon return one ResultSet each; run_query already wraps both into the list this function iterates. A column that the first row happens not to have is then left out of columns even though its values are sitting in rows. The table visualization builds its columns from data.columns (getColumns in client/app/services/query-result.js, then getOptions in viz-lib/src/visualizations/table/getOptions.ts), so those values never appear in the UI.

The fix records a column's type from the first non-null value a row supplies for it, and builds the result columns from column_names, the union the function was already computing. A column that no row supplies falls back to TYPE_STRING, which is what the old empty-result branch did. Single series results, including the empty one, come out exactly as before, so the two existing tests needed no changes.

How is this tested?

  • Unit tests (pytest, jest)

test_influxdb_columns_from_all_series in tests/query_runner/test_influx_db.py feeds one series with columns time, v1 and one with columns time, v2 plus the tag k1. Without the change it fails with columns holding only time and v1; with it, columns holds time, v1, v2 and k1 and the types are read from whichever row first supplies each value.

test_influxdb_column_types_from_first_non_null_value in the same file feeds a series whose first row is null in every value column, and checks that v1 takes TYPE_FLOAT from the next row of the same series, v2 takes TYPE_INTEGER from a later series, and v3, which is null in every row, keeps TYPE_STRING; without the change v1 and v2 are labeled strings.

$ python -m pytest tests/query_runner/test_influx_db.py -v
tests/query_runner/test_influx_db.py::test_influxdb_result_types_with_rows PASSED
tests/query_runner/test_influx_db.py::test_influxdb_result_types_with_no_rows_are_string PASSED
tests/query_runner/test_influx_db.py::test_influxdb_columns_from_all_series PASSED
tests/query_runner/test_influx_db.py::test_influxdb_column_types_from_first_non_null_value PASSED
4 passed

ruff check . at 0.0.287 and black --check . at 23.1.0, the versions the backend-lint job installs, are both clean, and so is black --check at the 24.4.2 that Restyled runs.

Related Tickets & Documents

None.

Review in cubic

_transform_result in redash/query_runner/influx_db.py collects the union
of column names and tag keys across every series, then throws that union
away and builds the result columns from the keys of the first row only.

When a query returns several series with different column sets, such as
"SELECT * FROM /^(cpu|mem)$/" or two statements separated by a semicolon,
any column missing from the first row is left out of "columns" even though
its values are present in "rows". The table visualization renders from
"columns", so those values never reach the user.

Record each column's type the first time a row supplies a value for it and
build the result columns from the union of column names, falling back to
TYPE_STRING for a column no row supplies. Results with a single series,
including the empty one, are unchanged.
@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

Greptile Summary

The PR fixes InfluxDB multi-series result transformation so columns appearing after the first row remain visible.

  • Builds result metadata from the union of columns and tag keys across all series.
  • Infers each column’s type from its first non-null value and defaults all-null columns to strings.
  • Adds coverage for differing multi-series schemas and leading null values.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
redash/query_runner/influx_db.py Preserves the complete multi-series column union and derives metadata from available non-null values.
tests/query_runner/test_influx_db.py Adds regression coverage for columns distributed across series and type inference after null values.

Reviews (2): Last reviewed commit: "InfluxDB: take column types from non-nul..." | Re-trigger Greptile

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread redash/query_runner/influx_db.py
_transform_result in redash/query_runner/influx_db.py records a column's type at the "if column not in column_types" test on line 62, from whichever row first supplies a value for that column. When that first value is None, _get_type returns TYPE_STRING because type(None) is not in TYPES_MAP, and the entry is never revisited, so a numeric column whose first value is null is labeled a string column.

InfluxDB produces null values for empty buckets, for example a GROUP BY time query over a range with gaps, and it returns several series whose column sets differ, so the row that first mentions a column often carries no value for it.

Record the type only from a non-null value. A column whose values are all null keeps the TYPE_STRING fallback that column_types.get already supplies. test_influxdb_column_types_from_first_non_null_value in tests/query_runner/test_influx_db.py covers a column whose first value is null and whose second is a float, a column supplied only by a later series, and a column that is null in every row.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant