# Changelog
## 0.3.0 — 2026-06-30
### Added
- **Per-shape prepared-statement cache names.** `insert_all/4` now derives the
Postgrex `:cache_statement` name from the table **and the unnest arity** (the
number of array columns), e.g. `ecto_unnest_all_events_2`. Ecto's default keys
`insert_all` on the table alone (`ecto_insert_all_events`), but `unnest` renders
different SQL per column shape, so distinct shapes for one table would otherwise
collide on one cache slot and force a re-prepare on every shape change. The row
count never enters the name (the SQL is constant across it). Pass
`cache_statement: "name"` to override, or `cache_statement: nil` to restore
Ecto's default. (Arity does not separate same-arity/different-column shapes — give
those an explicit `:cache_statement`.)
- **`:require_all_fields` completeness check.** With `require_all_fields: true`
every schema field must appear in the columns map or in `:placeholders`, else the
call raises listing what is missing — catching a forgotten column that would
otherwise silently take its DB default. The autogenerated primary key and
`timestamps()` fields are exempt. Schema sources only (a binary source has no
field list and raises). Defaults to `config :ecto_unnest, :require_all_fields`
(so it can be a test-only safety net), else `false`; an explicit per-call option
always wins.
## 0.2.0 — 2026-06-24
### Added
- **Per-row JSON columns (`jsonb` / `{:array, :map}`).** A column whose per-row
value is itself JSON is now shipped as a 1-D `text[]` of pre-encoded JSON with a
`::jsonb` cast in the `SELECT` projection (instead of a `::jsonb[]` param, which
`unnest` flattened and Postgrex double-encoded). Schema sources auto-detect
`:map` / `{:map, _}` / `{:array, :map}`; binary sources opt in with
`types: %{col: :jsonb}` or the new `:json` option. Raw terms are encoded with the
configured `:postgrex` `:json_library`; already-encoded strings pass through.
- **Full `Ecto.Query` as `:on_conflict`.** Enables a conditional
`ON CONFLICT ... DO UPDATE SET ... WHERE <predicate>` (and `ORDER BY`), matching
`Ecto.Repo.insert_all/3`.
- **`:types` accepts atoms** as well as strings.
- **`config :ecto_unnest, :allowed_types`** — an explicit allow-list of custom PG
types permitted in a `:types` override.
### Security
- **`:types` overrides are now fail-closed.** The value is rendered into the SQL
cast verbatim, so it is validated: Ecto's default PG types (`bigint`, `text`,
`jsonb`, `timestamptz`, …) are always allowed, but any other spelling (a domain,
an alias like `int4`, a modifier) must be listed in
`config :ecto_unnest, :allowed_types` or the call raises. Apps using `:types`
with non-default types (including binary sources, which require `:types`) must add
this config.
### Fixed
- **Placeholders no longer require a `pg_type`.** `resolve_pg_type!` is skipped for
placeholder (`:scalar`) columns, since their cast comes from `type(^value, type)`
in the projection, not the `unnest` param. Integer-backed `Ecto.Enum`, date and
uuid placeholders now work on schema sources with no `:types` entry.
- **Binary-source non-string placeholders** can be cast via `:types` (e.g.
`types: %{created_at: :timestamptz}`), including custom PG types such as a domain
`:kafka_topic_name`, rendered as a raw `::type` cast on the parameter.
### Notes
- Binary-source column alignment was confirmed correct on execution: both the
executed `Repo.insert_all/3` and `to_sql/3` derive the `INSERT` column list from
the same name-ordered `SELECT`, and the projection references `unnest` columns by
name — so values land in their named column regardless of physical column order.
A regression test guards this.