InfluxElixir.Client.Local.InfluxQL (InfluxElixir v0.1.37)

Copy Markdown View Source

The InfluxQL SELECT subset InfluxElixir.Client.Local answers, shaped the way InfluxDB 3 answers it (verified against the engine; see docs/design/2026-09-23_local-influxql.md).

InfluxQL is not SQL with other keywords. Every row carries "iox::measurement" and "time"; rows come back in time order; a row with no selected field value is dropped; an unknown column or measurement is an empty result, not an error; aggregates are named after the function (mean, count, ...) and put time at the epoch, except a lone selector (MAX, MIN, FIRST, LAST), which returns its point's time and tags; LIMIT and OFFSET apply per GROUP BY series.

This module is pure. parse/1 turns the statement into a query map; run/3 shapes rows the caller has already filtered with the statement's WHERE clause (Client.Local runs it through its SQL engine).

WHERE follows InfluxQL, not SQL (where_sql/2): a missing tag is the empty string (host != 'a' keeps points without host, host = '' finds them), =~ /re/ and !~ /re/ are unanchored matches on tags and false on fields, </> on a tag is false, durations (now() - 30m) are intervals, double-quoted identifiers are exact, and NOT is the engine's parse error. SHOW TAG VALUES [FROM m] WITH KEY = | != | =~ | !~ | IN (...) [WHERE ...] lists values as the engine does, over the last 24 hours unless the WHERE bounds time (parse_show_tag_values/1).

Refused by name, rather than answered wrongly: GROUP BY time(...) (the engine fills every empty bucket), fill(), INTO, SLIMIT/SOFFSET, subqueries, GROUP BY *, functions other than MEAN SUM COUNT MIN MAX FIRST LAST, F(*) other than COUNT(*), plain columns beside anything but a single selector, arithmetic in the select list, several measurements in FROM, sub-second durations, and LIMIT / OFFSET on SHOW TAG VALUES.

Summary

Types

A select item: every column, a column, or a function of a column.

Which tag keys a SHOW TAG VALUES lists.

A parsed SELECT.

Functions

Whether a tag key is one a key_filter/0 lists.

Whether a WHERE names time: then it, not the default window, bounds the rows.

Parses an InfluxQL SELECT. Returns {:error, message} for syntax the engine rejects and for the constructs listed in the moduledoc.

Parses SHOW TAG VALUES [FROM m] WITH KEY = k | != k | =~ /re/ | !~ /re/ | IN (k, ...) [WHERE ...], or nil when the statement is not one. LIMIT and OFFSET are refused by name: the engine applies them per measurement in an order the double does not reproduce.

Shapes rows — the measurement's points already filtered by the statement's WHERE, as SQL row maps with "time" — into the engine's answer. tags names the measurement's tag columns; everything else but time is a field.

Rewrites an InfluxQL WHERE into the caller's SQL, given the measurement's tag columns. {:error, message} for what the double refuses by name.

Types

item()

@type item() ::
  :star
  | {:column, binary(), binary()}
  | {:aggregate, binary(), binary() | :star, binary() | nil}

A select item: every column, a column, or a function of a column.

key_filter()

@type key_filter() ::
  {:eq, binary()}
  | {:ne, binary()}
  | {:in, [binary()]}
  | {:regex, Regex.t(), boolean()}

Which tag keys a SHOW TAG VALUES lists.

query()

@type query() :: %{
  items: [item()],
  measurement: binary(),
  where: binary() | nil,
  group_by: [binary()],
  descending: boolean(),
  limit: non_neg_integer() | nil,
  offset: non_neg_integer()
}

A parsed SELECT.

Functions

key_listed?(key, arg)

@spec key_listed?(binary(), key_filter()) :: boolean()

Whether a tag key is one a key_filter/0 lists.

mentions_time?(where)

@spec mentions_time?(binary() | nil) :: boolean()

Whether a WHERE names time: then it, not the default window, bounds the rows.

parse(statement)

@spec parse(binary()) :: {:ok, query()} | {:error, binary() | {:engine, binary()}}

Parses an InfluxQL SELECT. Returns {:error, message} for syntax the engine rejects and for the constructs listed in the moduledoc.

parse_show_tag_values(statement)

@spec parse_show_tag_values(binary()) ::
  nil
  | {:ok,
     %{measurement: binary() | nil, keys: key_filter(), where: binary() | nil}}
  | {:error, binary()}

Parses SHOW TAG VALUES [FROM m] WITH KEY = k | != k | =~ /re/ | !~ /re/ | IN (k, ...) [WHERE ...], or nil when the statement is not one. LIMIT and OFFSET are refused by name: the engine applies them per measurement in an order the double does not reproduce.

run(query, rows, tags)

@spec run(query(), [map()], MapSet.t(binary())) :: [map()]

Shapes rows — the measurement's points already filtered by the statement's WHERE, as SQL row maps with "time" — into the engine's answer. tags names the measurement's tag columns; everything else but time is a field.

where_sql(where, tags)

@spec where_sql(binary(), MapSet.t(binary())) :: {:ok, binary()} | {:error, binary()}

Rewrites an InfluxQL WHERE into the caller's SQL, given the measurement's tag columns. {:error, message} for what the double refuses by name.