# `Selecto.TypeSystem`

Type inference and coercion system for Selecto SQL expressions.

This module provides:
- Type inference for expressions (fields, functions, literals, complex expressions)
- Type compatibility checking for comparisons and set operations
- Type coercion rules for implicit type conversions
- Return type determination for SQL functions and aggregates

## Type Categories

Selecto organizes SQL types into the following categories:

- **Numeric**: `:integer`, `:bigint`, `:smallint`, `:decimal`, `:float`, `:numeric`
- **String**: `:string`, `:text`, `:varchar`, `:char`
- **Boolean**: `:boolean`
- **DateTime**: `:date`, `:time`, `:datetime`, `:utc_datetime`, `:naive_datetime`, `:timestamp`
- **JSON**: `:json`, `:jsonb`, `:map`
- **Array**: `{:array, inner_type}`
- **Binary**: `:binary`, `:bytea`
- **UUID**: `:uuid`, `:binary_id`
- **Spatial**: `:geometry`, `:geography`, `:point`, `:polygon`, etc.

## Usage

    # Infer type of an expression
    {:ok, :integer} = Selecto.TypeSystem.infer_type(selecto, {:count, "*"})
    {:ok, :decimal} = Selecto.TypeSystem.infer_type(selecto, {:sum, "price"})
    {:ok, :string} = Selecto.TypeSystem.infer_type(selecto, "product_name")

    # Check type compatibility
    true = Selecto.TypeSystem.compatible?(:integer, :decimal)
    false = Selecto.TypeSystem.compatible?(:string, :boolean)

    # Get coerced type for operation
    {:ok, :decimal} = Selecto.TypeSystem.coerce_types(:integer, :decimal, :arithmetic)

# `sql_type`

```elixir
@type sql_type() ::
  :unknown
  | :integer
  | :bigint
  | :smallint
  | :decimal
  | :float
  | :numeric
  | :string
  | :text
  | :varchar
  | :char
  | :boolean
  | :date
  | :time
  | :datetime
  | :utc_datetime
  | :naive_datetime
  | :timestamp
  | :json
  | :jsonb
  | :map
  | :binary
  | :bytea
  | :uuid
  | :binary_id
  | :geometry
  | :geography
  | :point
  | :linestring
  | :polygon
  | :multipoint
  | :multilinestring
  | :multipolygon
  | :geometrycollection
  | {:array, sql_type()}
```

# `type_category`

```elixir
@type type_category() ::
  :numeric
  | :string
  | :boolean
  | :datetime
  | :json
  | :array
  | :binary
  | :uuid
  | :spatial
  | :unknown
```

# `coerce_types`

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

Determine the result type when coercing two types for an operation.

## Operation Types
- `:arithmetic` - Numeric operations (+, -, *, /)
- `:comparison` - Comparison operations (=, <>, <, >, etc.)
- `:concatenation` - String concatenation (||)
- `:union` - Set operations (UNION, INTERSECT, EXCEPT)

# `compatible?`

```elixir
@spec compatible?(sql_type(), sql_type()) :: boolean()
```

Check if two types are compatible for comparisons or assignments.

# `datetime_type?`

```elixir
@spec datetime_type?(sql_type()) :: boolean()
```

Check if a type is a date/time type.

# `infer_type`

```elixir
@spec infer_type(Selecto.t(), term()) :: {:ok, sql_type()} | {:error, term()}
```

Infer the SQL type of an expression.

Returns `{:ok, type}` for successfully inferred types,
or `{:ok, :unknown}` when the type cannot be determined.

## Examples

    iex> infer_type(selecto, "product_name")
    {:ok, :string}

    iex> infer_type(selecto, {:count, "*"})
    {:ok, :bigint}

    iex> infer_type(selecto, {:sum, "price"})
    {:ok, :decimal}

# `infer_type!`

```elixir
@spec infer_type!(Selecto.t(), term()) :: sql_type()
```

Infer type and return just the type (for internal use where errors are handled upstream).

# `normalize_type`

```elixir
@spec normalize_type(atom()) :: sql_type()
```

Normalize Ecto types to Selecto's internal type representation.

# `numeric_type?`

```elixir
@spec numeric_type?(sql_type()) :: boolean()
```

Check if a type is numeric.

# `parse_sql_type`

```elixir
@spec parse_sql_type(String.t()) :: sql_type()
```

Parse a SQL type string into an atom type.

# `string_type?`

```elixir
@spec string_type?(sql_type()) :: boolean()
```

Check if a type is a string type.

# `type_category`

```elixir
@spec type_category(sql_type()) :: type_category()
```

Get the type category for a given SQL type.

---

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