# `Sublimate.SublimateHelpers`

Database access and SQL name-handling helper functions.

# `run_queries`

```elixir
@spec run_queries([String.t()], repo(), postgrex_options()) ::
  :ok | {:error, [Exception.t()]}
```

Takes a list of query instructions and calls `Ecto.Adapters.SQL.query/4` on each.

# `run_query`

```elixir
@spec run_query(
  String.t(),
  repo(),
  postgrex_options(),
  Ecto.Adapters.SQL.query_params() | nil
) :: {:ok, Postgrex.Result.t()} | {:error, Exception.t()}
```

Takes a single query instruction and calls `Ecto.Adapters.SQL.query/4` on it.

# `alias_column`

```elixir
@spec alias_column(String.t()) :: String.t()
```

Lowers a qualified `table.column` reference to `alias.column`, where the alias
is the unqualified table name.

## Examples

      iex> Sublimate.SublimateHelpers.alias_column("classifications.categories.id")
      "categories.id"

      iex> Sublimate.SublimateHelpers.alias_column("authors.role_id")
      "authors.role_id"

# `append_postgres_type`

```elixir
@spec append_postgres_type(String.t(), String.t()) :: String.t()
```

Appends the type to the content.

## Examples

      iex> Sublimate.SublimateHelpers.append_postgres_type("a", "text")
      "a::text"

# `distinct_table_names?`

```elixir
@spec distinct_table_names?(String.t(), String.t()) ::
  :ok | {:error, :table_names_not_distinct}
```

Checks whether two qualified table names are distinct.
Returns an error when the names are equal.

# `qualified_table_name`

```elixir
@spec qualified_table_name(String.t()) :: String.t()
```

Generates a qualified table name by joining the result of `split_prefix_and_table_name/1`.

## Examples

      iex> Sublimate.SublimateHelpers.qualified_table_name("articles")
      "public.articles"

      iex> Sublimate.SublimateHelpers.qualified_table_name("blog.articles")
      "blog.articles"

      iex> Sublimate.SublimateHelpers.qualified_table_name("  blog.articles  ")
      "blog.articles"

# `split_prefix_and_table_name`

```elixir
@spec split_prefix_and_table_name(String.t()) :: [String.t()]
```

Takes a table name that optionally contains a namespace prefix, and returns a 2-element list
with the table name and namespace.

## Examples

      iex> Sublimate.SublimateHelpers.split_prefix_and_table_name("articles")
      ["articles", "public"]

      iex> Sublimate.SublimateHelpers.split_prefix_and_table_name("blog.articles")
      ["articles", "blog"]

      iex> Sublimate.SublimateHelpers.split_prefix_and_table_name("  blog.articles  ")
      ["articles", "blog"]

# `table_alias`

```elixir
@spec table_alias(String.t()) :: String.t()
```

Returns the unqualified table name, used as the SQL alias for a table.

      iex> Sublimate.SublimateHelpers.table_alias("classifications.categories")
      "categories"

# `validate_identifier`

```elixir
@spec validate_identifier(String.t()) ::
  :ok | {:error, table_name_error_reason(), String.t()}
```

Validates PostgreSQL unquoted identifiers and qualified table names (schema.table).

Rules per https://www.postgresql.org/docs/17/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS:
- Must begin with a Unicode letter or underscore.
- Subsequent characters may be Unicode letters, underscores, digits, or dollar signs.
- Maximum 63 bytes (NAMEDATALEN - 1).
- Qualified names take the form "schema.table"; each part is validated independently.

## Examples

      iex> Sublimate.SublimateHelpers.validate_identifier("articles")
      :ok

      iex> Sublimate.SublimateHelpers.validate_identifier("public.articles")
      :ok

      iex> Sublimate.SublimateHelpers.validate_identifier("слон")
      :ok

      iex> Sublimate.SublimateHelpers.validate_identifier("1articles")
      {:error, :invalid_start, "1articles"}

      iex> Sublimate.SublimateHelpers.validate_identifier("public.articles.extra")
      {:error, :too_many_parts, "public.articles.extra"}

      iex> Sublimate.SublimateHelpers.validate_identifier(".articles")
      {:error, :empty_schema, ".articles"}

      iex> Sublimate.SublimateHelpers.validate_identifier("public.")
      {:error, :empty_table, "public."}

      iex> Sublimate.SublimateHelpers.validate_identifier("")
      {:error, :empty, ""}

# `wrap`

```elixir
@spec wrap(String.t() | term(), String.t()) :: String.t()
```

Wraps a string inside wrapping characters.
Returns the content parameter unchanged if:

- `content` is not a string
- `wrap_chars` is not a string

## Examples

      iex> Sublimate.SublimateHelpers.wrap("text", "''")
      "'text'"

      iex> Sublimate.SublimateHelpers.wrap("text", "{}")
      "{text}"

      iex > wrap(%{value: true}, "{}")
      %{value: true}

      iex> Sublimate.SublimateHelpers.wrap("text", nil)
      "text"

# `validate_postgrex_options`

```elixir
@spec validate_postgrex_options(postgrex_options()) :: keyword()
```

Extracts supported Postgrex options from the `postgrex_options` parameter.

## Examples

      iex> alias Sublimate.Test.Repo
      ...> postgrex_options = [
      ...>   timeout: 0,
      ...>   invalid: true
      ...> ]
      ...> Sublimate.SublimateHelpers.validate_postgrex_options(postgrex_options)
      [{:timeout, 0}]

# `fetch_table_columns`

```elixir
@spec fetch_table_columns(String.t(), repo(), postgrex_options()) ::
  {:ok, table_columns()} | {:error, Exception.t()}
```

Reads the column data from a table and returns it as a list of `Sublimate.TableColumnData` structs.

# `fetch_table_columns!`

```elixir
@spec fetch_table_columns!(String.t(), repo(), postgrex_options()) :: table_columns()
```

Calls `fetch_table_columns/3` and raises on error.

# `table_exists?`

```elixir
@spec table_exists?(String.t(), repo()) :: boolean()
```

Checks whether a table exists in the database.

## Examples

      iex> alias Sublimate.Test.Repo
      ...> Sublimate.SublimateHelpers.table_exists?("articles_facets", Repo)
      false

# `postgrex_options`

```elixir
@type postgrex_options() :: [{:timeout, integer() | :infinity}]
```

# `repo`

```elixir
@type repo() :: module()
```

# `table_columns`

```elixir
@type table_columns() :: [Sublimate.TableColumnData.t()]
```

# `table_name_error_reason`

```elixir
@type table_name_error_reason() ::
  :empty
  | :empty_schema
  | :empty_table
  | :too_long
  | :invalid_start
  | :invalid_characters
  | :too_many_parts
  | :invalid
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
