• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

akoutmos / sql_fmt / 23ce12ec2c137f7d53e6727cc251d3e758d0f447

10 Oct 2024 06:56PM UTC coverage: 94.444% (-5.6%) from 100.0%
23ce12ec2c137f7d53e6727cc251d3e758d0f447

push

github

akoutmos
Post processing Rust formatter to fix special character issues

5 of 6 new or added lines in 1 file covered. (83.33%)

17 of 18 relevant lines covered (94.44%)

30.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.5
/lib/sql_fmt.ex
1
defmodule SqlFmt do
2
  @moduledoc """
3
  This library provides an Elixir wrapper around the Rust
4
  [sqlformat](https://github.com/shssoichiro/sqlformat-rs) library. This allows you
5
  to efficiently format and pretty print SQL queries.
6
  """
7

8
  alias SqlFmt.FormatOptions
9
  alias SqlFmt.Native
10

11
  @doc """
12
  This function takes a query as a string along with optional formatting
13
  settings and returns the formatted query.
14

15
  For formatting settings look at the `SqlFmt.FormatOptions` module docs.
16
  """
17
  @spec format_query(query :: String.t()) :: {:ok, String.t()}
18
  @spec format_query(query :: String.t(), fmt_opts :: keyword()) :: {:ok, String.t()}
19
  def format_query(query, fmt_opts \\ []) do
20
    format_options = FormatOptions.new(fmt_opts)
42✔
21

22
    query
23
    |> Native.format(format_options)
24
    |> maybe_clean_special_character_operators()
42✔
25
  end
26

27
  @doc """
28
  This function takes a query as a string along with its parameters as a list of strings
29
  and optional formatting settings and returns the formatted query. As a note, when using this
30
  function all of the element in `query_params` must be strings and they all must be
31
  extrapolated out to their appropriate SQL values. For example query params of `["my_id"]`
32
  should be `["'my_id'"]` so that it is valid SQL when the parameter substitution takes place.
33

34
  For formatting settings look at the `SqlFmt.FormatOptions` module docs.
35
  """
36
  @spec format_query_with_params(query :: String.t(), query_params :: list(String.t())) :: {:ok, String.t()}
37
  @spec format_query_with_params(query :: String.t(), query_params :: list(String.t()), fmt_opts :: keyword()) ::
38
          {:ok, String.t()}
39
  def format_query_with_params(query, query_params, fmt_opts \\ []) do
40
    format_options = FormatOptions.new(fmt_opts)
8✔
41

42
    query
43
    |> Native.format(query_params, format_options)
44
    |> maybe_clean_special_character_operators()
8✔
45
  end
46

47
  # ---- Private helper functions ----
48

49
  @special_character_sequence_regex ~r/(\+|\-|\*|\/|\<|\>|\=|\~|\!|\@|\#|\%|\^|\&|\||\`|\?)\s(\+|\-|\*|\/|\<|\>|\=|\~|\!|\@|\#|\%|\^|\&|\||\`|\?)/
50

51
  # This is a work around the following bug in the Rust formatting
52
  # library https://github.com/shssoichiro/sqlformat-rs/issues/52
53
  #
54
  # 63 Regex replacement passes has been chosen as it is the default max
55
  # operator name as per the Postgres docs https://www.postgresql.org/docs/current/sql-createoperator.html.
56
  # The function will break early if no more replacements need to occur though.
57
  defp maybe_clean_special_character_operators({:ok, formatted_query}) do
58
    post_processed_query =
50✔
59
      1..63
60
      |> Enum.reduce_while(formatted_query, fn _pass, acc ->
61
        new_string = Regex.replace(@special_character_sequence_regex, acc, "\\g{1}\\g{2}")
58✔
62

63
        if acc == new_string do
58✔
64
          {:halt, new_string}
65
        else
66
          {:cont, new_string}
67
        end
68
      end)
69

70
    {:ok, post_processed_query}
71
  end
72

73
  defp maybe_clean_special_character_operators(other) do
NEW
74
    other
×
75
  end
76
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc