SQL parser for InfluxElixir.Client.Local.
Recognises the SQL subset documented on InfluxElixir.Client.Local and
produces a parsed_query/0 for the executor. It is deliberately strict:
anything the real InfluxDB v3 engine would reject — or that the double
cannot execute faithfully — is refused with a Client.Local:-prefixed 400
so a query cannot pass tests here and fail in production.
Pure functions only: no ETS, no connection state.
Summary
Types
Plain aggregates; :stddev/:var are the sample forms, as in InfluxDB.
An arithmetic expression inside an aggregate: a field reference, a numeric literal, or a binary operation over two expressions.
A WHERE operand: a column name, or an arithmetic expression over columns
and literals (price <= med * 3).
ORDER BY <column> [ASC|DESC]; the column may be time or any output alias.
One SELECT. ctes holds the WITH name AS (...) queries that precede
it, in order; measurement may name one of them.
A projected column: {source, output} where source is a column name or
an arithmetic expr/0 ((bid + ask) / 2 AS mid).
A time comparand: nanoseconds since the epoch, or now() plus an offset
in nanoseconds, resolved when the query runs.
A WHERE conjunction is a list of nodes: a predicate, an {:or, branches}
node whose branches are conjunctions, or a {:not, conjunction} node.
Functions
Parses a statement — an optional WITH list of non-recursive CTEs followed
by one SELECT — into a parsed_query/0.
Parses the WHERE ... clause (if any) out of the text after FROM <table>.
Substitutes $name placeholders with SQL literals built from params.
The first $name placeholder left in sql after substitution, or nil.
String literals are ignored: a substituted value that happens to contain
$ is text.
Types
@type aggregate() ::
:avg
| :sum
| :count
| :min
| :max
| :median
| :stddev
| :stddev_pop
| :var
| :var_pop
Plain aggregates; :stddev/:var are the sample forms, as in InfluxDB.
An arithmetic expression inside an aggregate: a field reference, a numeric literal, or a binary operation over two expressions.
A WHERE operand: a column name, or an arithmetic expression over columns
and literals (price <= med * 3).
@type order_by() :: {binary(), :asc | :desc} | nil
ORDER BY <column> [ASC|DESC]; the column may be time or any output alias.
@type parsed_query() :: %{ measurement: binary(), where: [where_node()], order_by: order_by(), limit: pos_integer() | nil, group_by_interval: pos_integer() | nil, group_by_columns: [binary()] | nil, select_columns: [select_column()] | nil, distinct_columns: [binary()] | nil, projection_columns: [projection()] | nil, ctes: [{binary(), parsed_query()}], cross_join: {binary(), [binary()]} | nil }
One SELECT. ctes holds the WITH name AS (...) queries that precede
it, in order; measurement may name one of them.
A projected column: {source, output} where source is a column name or
an arithmetic expr/0 ((bid + ask) / 2 AS mid).
@type select_column() :: {:time_bucket, binary()} | {:aggregate, aggregate(), expr(), binary()} | {:count_star, binary()} | {:count_distinct, binary(), binary()} | {:ordered_aggregate, :first | :last, binary(), binary(), binary()} | {:selector, :first | :last | :min | :max, binary(), binary(), :value | :time, binary()} | {:grouping_column, binary(), binary()}
A time comparand: nanoseconds since the epoch, or now() plus an offset
in nanoseconds, resolved when the query runs.
@type where_node() :: where_clause() | {:or, [[where_node()]]} | {:not, [where_node()]}
A WHERE conjunction is a list of nodes: a predicate, an {:or, branches}
node whose branches are conjunctions, or a {:not, conjunction} node.
@type where_op() ::
:eq
| :gt
| :lt
| :gte
| :lte
| :ne
| :in
| :not_in
| :is_null
| :is_not_null
| :between
| :not_between
| :like
| :not_like
Functions
@spec parse_select(binary()) :: {:ok, parsed_query()} | {:error, term()}
Parses a statement — an optional WITH list of non-recursive CTEs followed
by one SELECT — into a parsed_query/0.
@spec parse_where(binary()) :: {:ok, [where_node()]} | {:error, map()}
Parses the WHERE ... clause (if any) out of the text after FROM <table>.
Substitutes $name placeholders with SQL literals built from params.
The first $name placeholder left in sql after substitution, or nil.
String literals are ignored: a substituted value that happens to contain
$ is text.