Packages
exosql
0.2.21
0.2.88
0.2.87
0.2.86
0.2.85
0.2.84
0.2.83
0.2.82
0.2.81
0.2.80
0.2.79
0.2.78
0.2.77
0.2.76
0.2.75
0.2.74
0.2.73
0.2.72
0.2.71
0.2.70
0.2.69
0.2.68
0.2.67
0.2.65
0.2.64
0.2.63
0.2.62
0.2.60
0.2.59
0.2.58
0.2.57
0.2.56
0.2.55
0.2.54
0.2.53
0.2.51
0.2.50
0.2.49
0.2.48
0.2.47
0.2.46
0.2.45
0.2.44
0.2.43
0.2.42
0.2.41
0.2.40
0.2.39
0.2.38
0.2.37
0.2.36
0.2.35
0.2.33
0.2.32
0.2.31
0.2.30
0.2.29
0.2.28
0.2.26
0.2.25
0.2.24
0.2.23
0.2.22
0.2.21
0.2.20
0.2.19
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
Universal SQL engine for Elixir. This library implements the SQL logic to perform queries on user provided databases using a simple interface based on Foreign Data Wrappers from PostgreSQL.
Current section
Files
Jump to
Current section
Files
lib/expr.ex
require Logger
defmodule ExoSQL.Expr do
@moduledoc """
Expression executor.
Requires a simplified expression from `simplify_expr_columns` that converts
columns names to column positions, and then use as:
```
iex> row = [1,2,3,4,5]
iex> expr = {:op, {"*", {:column, 1}, {:column, 2}}}
iex> ExoSQL.Expr.run_expr(expr, row)
6
```
"""
import ExoSQL.Utils, only: [to_number: 1]
def run_expr({:op, {"and", op1, op2}}, cur) do
r1 = run_expr(op1, cur)
r2 = run_expr(op2, cur)
r1 && r2
end
def run_expr({:op, {"AND", op1, op2}}, cur) do
r1 = run_expr(op1, cur)
r2 = run_expr(op2, cur)
r1 && r2
end
def run_expr({:op, {"or", op1, op2}}, cur) do
r1 = run_expr(op1, cur)
r2 = run_expr(op2, cur)
r1 || r2
end
def run_expr({:op, {"OR", op1, op2}}, cur) do
r1 = run_expr(op1, cur)
r2 = run_expr(op2, cur)
r1 || r2
end
def run_expr({:op, {"=", op1, op2}}, cur) do
r1 = run_expr(op1, cur)
r2 = run_expr(op2, cur)
{r1, r2} = match_types(r1, r2)
r1 == r2
end
def run_expr({:op, {">", op1, op2}}, cur) do
r1 = run_expr(op1, cur)
r2 = run_expr(op2, cur)
{r1, r2} = match_types(r1, r2)
case {r1, r2} do
{%DateTime{}, %DateTime{}} ->
DateTime.compare(r1, r2) == :gt
_ ->
with {:ok, n1} <- to_number(r1),
{:ok, n2} <- to_number(r2) do
n1 > n2
else
{:error, _} ->
r1 > r2
end
end
end
def run_expr({:op, {">=", op1, op2}}, cur) do
r1 = run_expr(op1, cur)
r2 = run_expr(op2, cur)
{r1, r2} = match_types(r1, r2)
case {r1, r2} do
{%DateTime{}, %DateTime{}} ->
DateTime.compare(r1, r2) == :eq
{a, b} ->
with {:ok, n1} <- to_number(r1),
{:ok, n2} <- to_number(r2) do
n1 >= n2
else
{:error, _} ->
a >= b
end
end
end
def run_expr({:op, {"==", op1, op2}}, cur), do: run_expr({:op, {"=", op1, op2}}, cur)
def run_expr({:op, {"!=", op1, op2}}, cur), do: not run_expr({:op, {"=", op1, op2}}, cur)
def run_expr({:op, {"<", op1, op2}}, cur), do: not run_expr({:op, {">=", op1, op2}}, cur)
def run_expr({:op, {"<=", op1, op2}}, cur), do: not run_expr({:op, {">", op1, op2}}, cur)
def run_expr({:op, {"*", op1, op2}}, cur) do
{:ok, n1} = to_number(run_expr(op1, cur))
{:ok, n2} = to_number(run_expr(op2, cur))
n1 * n2
end
def run_expr({:op, {"+", op1, op2}}, cur) do
{:ok, n1} = to_number(run_expr(op1, cur))
{:ok, n2} = to_number(run_expr(op2, cur))
n1 + n2
end
def run_expr({:op, {"-", op1, op2}}, cur) do
{:ok, n1} = to_number(run_expr(op1, cur))
{:ok, n2} = to_number(run_expr(op2, cur))
n1 - n2
end
def run_expr({:op, {"||", op1, op2}}, cur) do
s1 = to_string(run_expr(op1, cur))
s2 = to_string(run_expr(op2, cur))
s1<>s2
end
def run_expr({:fn, {fun, exprs}}, cur) do
params = for e <- exprs, do: run_expr(e, cur)
ExoSQL.Builtins.call_function(fun, params)
end
def run_expr({:pass, val}, _cur), do: val
def run_expr({:lit, val}, _cur), do: val
def run_expr({:column, n}, cur) when is_number(n) do
Enum.at(cur, n)
end
@doc """
Try to return matching types.
* If any is datetime, return datetimes
* If any is number, return numbers
* Otherwise, as is
"""
def match_types(a, b) do
case {a, b} do
{t1, t2} when is_number(t1) and is_number(t2) ->
{a, b}
{%DateTime{}, _} ->
{a, ExoSQL.Builtins.to_datetime(b)}
{_, %DateTime{}} ->
{ExoSQL.Builtins.to_datetime(a), b}
{t1, _} when is_number(t1) ->
{:ok, t2} = to_number(b)
{t1, t2}
{_, t2} when is_number(t2) ->
{:ok, t1} = to_number(a)
{t1, t2}
_other ->
{a, b}
end
end
end