Sublimate.SublimateHelpers (Sublimate v0.1.0)

Copy Markdown

Database access and SQL name-handling helper functions.

Summary

Database functions

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

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

Name handling

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

Appends the type to the content.

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

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

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

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

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

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

Option handling

Extracts supported Postgrex options from the database_options parameter.

Extracts the repo value from the database_options parameter. If that doesn't exist, tries to fetch it from the application configuration.

Table properties

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

Checks whether a table exists in the database.

Database functions

run_queries(queries, repo, postgrex_options)

@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(sql, repo, postgrex_options, params \\ [])

@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.

Name handling

alias_column(qualified_column)

@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(content, type)

@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?(qualified_table_name_a, qualified_table_name_b)

@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(table_with_prefix)

@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(table_with_prefix)

@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(qualified_table)

@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_table_name(name)

@spec validate_table_name(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_table_name("articles")
  :ok

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

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

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

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

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

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

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

wrap(content, wrap_chars)

@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"

Option handling

get_postgrex_options(database_options)

@spec get_postgrex_options(database_options()) :: keyword()

Extracts supported Postgrex options from the database_options parameter.

Examples

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

get_repo(opts)

@spec get_repo(database_options()) :: repo()

Extracts the repo value from the database_options parameter. If that doesn't exist, tries to fetch it from the application configuration.

Table properties

fetch_table_columns(table, repo, postgrex_options)

@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!(table, repo, postgrex_options)

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

Calls fetch_table_columns/3 and raises on error.

table_exists?(qualified_table_name, repo)

@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

Types

database_options()

@type database_options() :: [
  {:repo, repo()} | {:otp_app, atom()} | postgrex_options()
]

postgrex_options()

@type postgrex_options() :: [timeout: integer() | :infinity, log: boolean()]

repo()

@type repo() :: module()

table_columns()

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

table_name_error_reason()

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