Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
on: push
jobs:
test:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
name: OTP 27 / Elixir 1.18
services:
postgres:
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
on: push
jobs:
test:
runs-on: ubuntu-22.04
name: OTP 27 / Elixir 1.18
services:
postgres:
image: postgres:latest
env:
POSTGRES_PASSWORD: postgres
ports: ["5432:5432"]
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Checkout ${{github.repository}}
uses: actions/checkout@v4
- name: Checkout sqltest
uses: actions/checkout@v4
with:
path: sqltest
repository: elliotchance/sqltest
- uses: erlef/setup-beam@v1
with:
otp-version: 27
elixir-version: 1.18
- uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-
- run: mix deps.get && mix sql.gen.test sqltest/standards/2016 && mix test
71 changes: 71 additions & 0 deletions lib/mix/tasks/sql.gen.test.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2025 DBVisor

defmodule Mix.Tasks.Sql.Gen.Test do
use Mix.Task
import Mix.Generator

@shortdoc "Generates test from the BNF rules"
@shortdoc since: "0.2.0"

def run([base]) do
create_file("test/conformance/e_test.exs", test_template([mod: SQL.Conformance.ETest, dir: Path.join(base, "E")]))
create_file("test/conformance/f_test.exs", test_template([mod: SQL.Conformance.FTest, dir: Path.join(base, "F")]))
create_file("test/conformance/s_test.exs", test_template([mod: SQL.Conformance.STest, dir: Path.join(base, "S")]))
create_file("test/conformance/t_test.exs", test_template([mod: SQL.Conformance.TTest, dir: Path.join(base, "T")]))
end

def generate_test(dir) do
for path <- File.ls!(dir), path =~ ".tests.yml", [{~c"feature", feature}, {~c"id", id}, {~c"sql", sql}] <- :yamerl.decode_file(to_charlist(Path.join(dir, path))) do
statements = if is_list(hd(sql)), do: sql, else: [sql]
statements = Enum.map(statements, &String.replace(to_string(&1), ~r{(VARING)}, "VARYING"))
{"#{feature} #{id}", Enum.map(statements, &{trim(&1), &1})}
end
end

def trim(value) do
value
|> String.replace(~r{\(\s+\b}, &String.replace(&1, " ", ""))
|> String.replace(~r{\(\s+'}, &String.replace(&1, " ", ""))
|> String.replace(~r{\(\s+"}, &String.replace(&1, " ", ""))
|> String.replace(~r{\(\s+\*}, &String.replace(&1, " ", ""))
|> String.replace(~r{[[:alpha:]]+\s+\(}, &String.replace(&1, " ", ""))
|> String.replace(~r{\b\s+\,}, &String.replace(&1, " ", ""))
|> String.replace(~r{\)\s+\,}, &String.replace(&1, " ", ""))
|> String.replace(~r{\'\s+\,}, &String.replace(&1, " ", ""))
|> String.replace(~r{\b\s+\)}, &String.replace(&1, " ", ""))
|> String.replace(~r{'\s+\)}, &String.replace(&1, " ", ""))
|> String.replace(~r{\*\s+\)}, &String.replace(&1, " ", ""))
|> String.replace(~r{\)\s+\)}, &String.replace(&1, " ", ""))
|> String.replace(~r{\W(SELECT|REFERENCES|INSERT|UPDATE|IN|MYTEMP)\(}, &Enum.join(Regex.split(~r{\(}, &1, include_captures: true, trim: true), " "))
|> String.replace(~r{^(SELECT)\(}, &Enum.join(Regex.split(~r{\(}, &1, include_captures: true, trim: true), " "))
|> String.replace(~r{\s+\.\s+}, &String.replace(&1, " ", ""))
|> String.replace(~r{\d\s(\+|\-)\d}, &Enum.join(Enum.map(Regex.split(~r{\+|\-}, &1, include_captures: true, trim: true), fn x -> String.trim(x) end), " "))
|> String.trim()
end

embed_template(:test, """
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2025 DBVisor

defmodule <%= inspect @mod %>.Adapter do
use SQL.Token

def token_to_string(value, mod \\\\ __MODULE__)
def token_to_string(value, _mod) when is_atom(value), do: String.upcase(Atom.to_string(value))
def token_to_string(token, mod), do: SQL.Adapters.ANSI.token_to_string(token, mod)
end
defmodule <%= inspect @mod %> do
use ExUnit.Case, async: true
use SQL, adapter: <%= inspect @mod %>.Adapter

<%= for {name, statements} <- generate_test(@dir) do %>
test <%= inspect name %> do
<%= for {left, right} <- statements do %>
assert ~s{<%= left %>} == to_string(~SQL[<%= right %>])
<% end %>
end
<% end %>
end
""")
end
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule SQL.MixProject do
name: "SQL",
docs: docs(),
package: package(),
aliases: [bench: "run bench.exs"]
aliases: ["sql.bench": "run bench.exs"]
]
end

Expand Down Expand Up @@ -44,6 +44,7 @@ defmodule SQL.MixProject do
{:ecto_sql, "~> 3.12", only: [:dev, :test]},
{:ex_doc, "~> 0.37", only: :dev},
{:postgrex, ">= 0.0.0", only: [:dev, :test]},
{:yamerl, ">= 0.0.0", only: [:dev, :test]},
]
end
end
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
"postgrex": {:hex, :postgrex, "0.20.0", "363ed03ab4757f6bc47942eff7720640795eb557e1935951c1626f0d303a3aed", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "d36ef8b36f323d29505314f704e21a1a038e2dc387c6409ee0cd24144e187c0f"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
}