Skip to content

Instantly share code, notes, and snippets.

@zachdaniel
Created September 4, 2024 02:16
Show Gist options
  • Save zachdaniel/4df0b35185b1bc729b8a420703e0694c to your computer and use it in GitHub Desktop.
Save zachdaniel/4df0b35185b1bc729b8a420703e0694c to your computer and use it in GitHub Desktop.
A really shitty parser that gets a list of string expressions from a Postgres index creation definition
# CREATE INDEX users_lower_email_idx ON public.users USING btree (lower((email)::text))
# CREATE INDEX unique_email_com3 ON public.users USING btree (email, id) WHERE (email ~~ '%.com'::citext)
defp parse_columns_from_index_def(string, using) do
string
|> String.trim_leading("CREATE ")
|> String.trim_leading("UNIQUE ")
|> String.trim_leading("INDEX ")
|> String.replace(~r/^[a-zA-Z0-9_\.]+\s/, "")
|> String.trim_leading("ON ")
|> String.replace(~r/^[\S]+/, "")
|> String.trim_leading()
|> String.trim_leading("USING #{using} ")
|> do_parse_columns()
|> then(&{:ok, &1})
catch
:error -> :error
end
def parse_columns(char) do
do_parse_columns(char)
end
defp do_parse_columns(char, state \\ [], field \\ "", acc \\ [])
defp do_parse_columns("(" <> rest, [], field, acc) do
do_parse_columns(rest, [:outer], field, acc)
end
defp do_parse_columns(")" <> _rest, [:outer], field, acc) do
if field == "" do
Enum.reverse(acc)
else
Enum.reverse([field | acc])
end
end
defp do_parse_columns("(" <> rest, [:outer], field, acc) do
do_parse_columns(rest, [:in_paren, :in_field, :outer], field, acc)
end
defp do_parse_columns(", " <> rest, [:in_field, :outer], field, acc) do
do_parse_columns(rest, [:in_field, :outer], "", [field | acc])
end
defp do_parse_columns(<<str::binary-size(1)>> <> rest, [:outer], field, acc) do
do_parse_columns(rest, [:in_field, :outer], field <> str, acc)
end
defp do_parse_columns("''" <> rest, [:in_quote | stack], field, acc) do
do_parse_columns(rest, [:in_quote | stack], field <> "'", acc)
end
defp do_parse_columns("'" <> rest, [:in_quote | stack], field, acc) do
do_parse_columns(rest, stack, field <> "'", acc)
end
defp do_parse_columns(<<str::binary-size(1)>> <> rest, [:in_quote | stack], field, acc) do
do_parse_columns(rest, [:in_quote | stack], field <> str, acc)
end
defp do_parse_columns("'" <> rest, stack, field, acc) do
do_parse_columns(rest, [:in_quote | stack], field <> "'", acc)
end
defp do_parse_columns("(" <> rest, stack, field, acc) do
do_parse_columns(rest, [:in_paren | stack], field <> "(", acc)
end
defp do_parse_columns(")" <> rest, [:in_paren | stack], field, acc) do
do_parse_columns(rest, stack, field <> ")", acc)
end
defp do_parse_columns("), " <> rest, [:in_field | stack], field, acc) do
do_parse_columns(rest, [:in_field | stack], "", [field | acc])
end
defp do_parse_columns(")" <> _rest, [:in_field | _stack], field, acc) do
Enum.reverse([field | acc])
end
defp do_parse_columns(<<str::binary-size(1)>> <> rest, [:in_paren | stack], field, acc) do
do_parse_columns(rest, [:in_paren | stack], field <> str, acc)
end
defp do_parse_columns(<<str::binary-size(1)>> <> rest, [:outer], field, acc) do
do_parse_columns(rest, [:in_field, :outer], field <> str, acc)
end
defp do_parse_columns(<<str::binary-size(1)>> <> rest, [:in_field | stack], field, acc) do
do_parse_columns(rest, [:in_field | stack], field <> str, acc)
end
defp do_parse_columns(", " <> rest, [:in_field | stack], field, acc) do
do_parse_columns(rest, stack, "", [field | acc])
end
defp do_parse_columns(")" <> _rest, [:outer], field, acc) do
Enum.reverse([field | acc])
end
defp do_parse_columns("", [:in_field | _stack], field, acc) do
Enum.reverse([field | acc])
end
defp do_parse_columns("", [:outer], field, acc) do
if field == "" do
Enum.reverse(acc)
else
Enum.reverse([field | acc])
end
end
defp do_parse_columns(other, stack, field, acc) do
raise "Unexpected character: #{inspect(other)} at #{inspect(stack)} with #{inspect(field)} - #{inspect(acc)}"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment