Packages

SQL provides state-of-the-art, high-performance SQL integration for Elixir, built to handle extreme concurrency with unmatched expressiveness and ergonomic query composition. Write safe, composable, parameterized queries directly, without translating to Ecto or any ORM.

Current section

Files

Jump to
sql lib token.ex
Raw

lib/token.ex

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2025 DBVisor
defmodule SQL.Token do
@moduledoc false
@doc """
Returns a SQL iodata for a given token.
"""
@doc since: "0.3.0"
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
@compile {:inline, to_iodata: 4, __to_iodata__: 4, indention: 3, indention: 4, keyword: 2}
@doc false
def to_iodata(token, %{format: format, case: case, binding: binding}) do
Process.put(:sql_binding, binding)
to_iodata(token, format, case, [])
end
defp to_iodata(token, format, case, acc), do: __to_iodata__(token, format, case, acc)
defp indention(acc, format, [{_,{_,_,_,_,l,c}}|_]) do
indention(acc, format, l, c)
end
defp indention(acc, format, [{_,{_,_,_,_,_,_,l,c}}|_]) do
indention(acc, format, l, c)
end
defp indention([<<_::binary>>|_]=acc, :dynamic, 0, 0), do: [?\s|acc]
defp indention(acc, _format, 0, 0), do: acc
defp indention(acc, _format, 0, 1), do: [?\s|acc]
defp indention(acc, _format, 1, 0), do: [?\n|acc]
defp indention(acc, format, line, 0=column), do: indention([?\n|acc], format, line-1, column)
defp indention(acc, format, line, column), do: indention([?\s|acc], format, line, column-1)
defp keyword(:upper, value), do: String.upcase(Atom.to_string(value))
defp keyword(:lower, value), do: Atom.to_string(value)
{reserved, non_reserved, operators} = SQL.BNF.get_rules()
for atom <- Enum.uniq(Enum.map(reserved++non_reserved++operators,&elem(&1, 0))) do
defp __to_iodata__(unquote(atom), _format, case, acc) do
[keyword(case, unquote(atom))|acc]
end
end
defp __to_iodata__(:comma, _format, _case, acc) do
[?,|acc]
end
defp __to_iodata__(:dot, _format, _case, acc) do
[?.|acc]
end
defp __to_iodata__(:bracket, _format, _case, acc) do
[?[,?]|acc]
end
defp __to_iodata__({:paren, [{_, {_,_,_,_,l,c,_,_}}|_]=m, []}, format, case, acc) do
indention([?(,?)|acc], format, l, c)
end
defp __to_iodata__({:colon, m, values}, format, case, acc) do
to_iodata(values, format, case, indention([?;|acc], format, m))
end
defp __to_iodata__({:comma, m, values}, format, case, acc) do
indention([?,|to_iodata(values, format, case, acc)], format, m)
end
defp __to_iodata__({:comment, m, value}, format, _case, acc) do
indention([?-,?-,value|acc], format, m)
end
defp __to_iodata__({:comments, m, value}, format, _case, acc) do
indention([?\\,?*,value,?*,?\\|acc], format, m)
end
defp __to_iodata__({:quote, m, value}, format, _case, acc) do
indention([?',value,?'|acc], format, m)
end
defp __to_iodata__({:backtick, m, value}, format, _case, acc) do
indention([?`,value,?`|acc], format, m)
end
defp __to_iodata__({tag, m, []}, format, case, acc) do
indention(to_iodata(tag, format, case, acc), format, m)
end
defp __to_iodata__({:case=tag, [{_, {_,_,_,_,l,c,_,_}}|_]=m, values}, format, case, acc) do
indention([to_iodata(tag, format, case, [])|to_iodata(values, format, case, indention(to_iodata(:end, format, case, acc), format, m))], format, l, c)
end
defp __to_iodata__({:paren, [{_, {_,_,_,_,l,c,_,_}}|_]=m, values}, format, case, acc) do
indention([?(|to_iodata(values, format, case, indention([?)|acc], format, m))], format, l, c)
end
defp __to_iodata__({:bracket=tag, [{_, {l, c, _, _, _, _, _, _}}|_]=m, [{_, [{_, {l, cc,_,_,_,_}}|_], _}]=values}, format, case, acc) when c >= cc do
to_iodata(values, format, case, indention(to_iodata(tag, format, case, acc), format, m))
end
defp __to_iodata__({:bracket, [{_, {_,_,_,_,l,c,_,_}}|_]=m, values}, format, case, acc) do
indention([?[|to_iodata(values, format, case, indention([?]|acc], format, m))], format, l,c)
end
defp __to_iodata__({:brace, [{_, {_,_,_,_,l,c,_,_}}|_]=m, values}, format, case, acc) do
indention([?{|to_iodata(values, format, case, indention([?}|acc], format, m))], format, l,c)
end
defp __to_iodata__({_, [], values}, format, case, acc) do
to_iodata(values, format, case, acc)
end
defp __to_iodata__({:ident, [_,_,{:tag, tag}|_]=m, [{:paren, _, _}]=values}, format, case, acc) do
indention(to_iodata(tag, format, case, to_iodata(values, format, case, acc)), format, m)
end
defp __to_iodata__({:"::"=tag, [_,{_, type}|_]=m, [left, right]}, format, case, acc) do
to_iodata(left, format, case, to_iodata(tag, format, case, to_iodata(right, format, case, acc)))
end
defp __to_iodata__({tag, [{_, {l,c,_,_,_,_}}|_]=m, [{_, [{_, {ll,cc,_,_,_,_}}|_], _}]=values}, format, case, acc) when l >= ll and c >= cc do
to_iodata(values, format, case, indention(to_iodata(tag, format, case, acc), format, m))
end
defp __to_iodata__({tag, [_,{_, type}|_]=m, [left, right]}, format, case, acc) when type == :operator or tag in ~w[between cursor for to union except intersect over]a do
to_iodata(left, format, case, indention(to_iodata(tag, format, case, to_iodata(right, format, case, acc)), format, m))
end
defp __to_iodata__({:ident, m, value}=node, format, :upper, acc) do
indention([:string.uppercase(value)|acc], format, m)
end
defp __to_iodata__({tag, m, [{_,_,_}|_]=values}, format, case, acc) do
indention(to_iodata(tag, format, case, to_iodata(values, format, case, acc)), format, m)
end
defp __to_iodata__({tag, m, value}=node, format, _case, acc) when tag in ~w[ident numeric special]a do
indention([value|acc], format, m)
end
defp __to_iodata__({:double_quote, m, value}=node, format, _case, acc) do
indention([?", value, ?"|acc], format, m)
end
defp __to_iodata__(atom, _format, case, acc) when is_atom(atom) do
[keyword(case, atom)|acc]
end
defp __to_iodata__([token|tokens], format, case, acc) do
to_iodata(token, format, case, to_iodata(tokens, format, case, acc))
end
defp __to_iodata__([], _format, _case, acc) do
acc
end
defoverridable to_iodata: 4
end
end
end