# `Lotus.SQL.Identifier`
[🔗](https://github.com/elixir-lotus/lotus/blob/v1.2.0/lib/lotus/sql/identifier.ex#L1)

Parses and validates SQL identifiers before they are interpolated into a
statement.

Names that reach these functions come from schema introspection or from a
caller's table reference, and adapters quote them on their way into SQL.
Quoting alone does not stop a name such as `users"; DROP TABLE` from
breaking out of the quotes on engines with lax quoting rules, so every
identifier must match `[a-zA-Z_][a-zA-Z0-9_]*` first. Adapters call
`validate_identifier/2` and `validate_table_parts/2` from their
`validate_identifier/3` and `parse_qualified_name/2` callbacks, and the
Postgres dialect checks a configured `search_path` with
`validate_search_path!/1`.

# `parse_table_name`

```elixir
@spec parse_table_name(String.t()) :: {String.t() | nil, String.t()}
```

Parses a potentially schema-qualified table name into `{schema, table}`.

Returns `{nil, table}` for unqualified names.

# `validate_identifier`

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

Validates that a string is a safe SQL identifier.

Returns `:ok` if the identifier matches `[a-zA-Z_][a-zA-Z0-9_]*`,
or `{:error, reason}` otherwise.

# `validate_identifier!`

```elixir
@spec validate_identifier!(String.t(), String.t()) :: :ok
```

Validates that a string is a safe SQL identifier, raising on failure.

# `validate_search_path!`

```elixir
@spec validate_search_path!(String.t()) :: :ok
```

Validates a Postgres search_path string.

Splits on `,`, trims whitespace, and validates each part as an identifier.
Raises `ArgumentError` if any part is invalid.

# `validate_table_parts`

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

Validates all parts of a table reference (schema + table, or just table).

---

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