- [Ecto.Query] Add support for subqueries operators:
all
,any
, andexists
- [Ecto.Changeset] Use association source on
put_assoc
with maps/keywords - [Ecto.Enum] Add
cast
clause for nil values onEcto.Enum
- [Ecto.Schema] Allow nested type
:any
for non-virtual fields
- [mix ecto.drop] Provide
--force-drop
for databases that may support it - [guides] Add new "Multi tenancy with foreign keys" guide
- [Ecto.Changeset] Make keys optionals in specs
- [Ecto.Enum] Make sure
values/2
works for virtual fields - [Ecto.Query] Fix missing type on CTE queries that select a single field
- [Ecto.Query] Do not reset parameter counter for nested CTEs
- [Ecto.Type] Fix regression where array type with nils could no longer be cast/load/dump
- [Ecto.Type] Fix CaseClauseError when casting a decimal with a binary remainder
- [Ecto.Repo] Add Repo.reload/2 and Repo.reload!/2
- [Ecto.Changeset] Fix "schema/1 is undefined or private" error while inspecting a schemaless changeset
- [Ecto.Repo] Invoke
c:Ecto.Repo.default_options/1
per entry-point operation
- [Ecto.Changeset] Warn if there are duplicate IDs in the parent schema for
cast_assoc/3
/cast_embed/3
- [Ecto.Schema] Allow
belongs_to
to accept options for parameterized types
- [Ecto.Query] Keep field types when using a subquery with source
v3.5 requires Elixir v1.8+.
- [Ecto.Changeset] Ensure
:empty_values
incast/4
does not automatically propagate to following cast calls. If you want a given set of:empty_values
to apply to allcast/4
calls, change the value stored inchangeset.empty_values
instead - [Ecto.Changeset] Do not force repository updates to happen when using
optimistic_lock
- [Ecto.Changeset] Do not automatically share empty values across
cast/3
calls - [Ecto.Query] Consider query prefix in cte/combination query cache
- [Ecto.Query] Allow the entry to be marked as nil when using left join with subqueries
- [Ecto.Query] Support subqueries inside dynamic expressions
- [Ecto.Repo] Fix preloading when using dynamic repos and the sandbox in automatic mode
- [Ecto.Repo] Do not duplicate collections when associations are preloaded for repeated elements
- [Ecto.Enum] Add
Ecto.Enum
as a custom parameterized type - [Ecto.Query] Allow
:prefix
infrom
to be set to nil - [Ecto.Query] Do not restrict subqueries in
where
to map/struct types - [Ecto.Query] Allow atoms in query without interpolation in order to support Ecto.Enum
- [Ecto.Schema] Do not validate uniqueness if there is a prior error on the field
- [Ecto.Schema] Allow
redact: true
infield
- [Ecto.Schema] Support parameterized types via
Ecto.ParameterizedType
- [Ecto.Query] Allow
count/0
ontype/2
- [Ecto.Multi] Support anonymous functions in multiple functions
- [Ecto.Query] Consider booleans as literals in unions, subqueries, ctes, etc
- [Ecto.Schema] Generate IDs for nested embeds
- [Ecto.Changeset] Allow custom error key in
unsafe_validate_unique
- [Ecto.Changeset] Improve performance when casting large params maps
- [Ecto.Changeset] Improve error message for invalid
cast_assoc
- [Ecto.Query] Fix inspecting query with fragment CTE
- [Ecto.Query] Fix inspecting dynamics with aliased bindings
- [Ecto.Query] Improve error message when selecting a single atom
- [Ecto.Repo] Reduce data-copying when preloading multiple associations
- [Ecto.Schema] Do not define a compile-time dependency for schema in
:join_through
- [Ecto.Schema] Add
join_where
support tomany_to_many
- [Ecto.Query] Support
as/1
andparent_as/1
for lazy named bindings and to allow parent references from subqueries - [Ecto.Query] Support
x in subquery(query)
- [Ecto.Query] Do not raise for missing assocs if :force is given to preload
- [Ecto.Repo] Return error from
Repo.delete
on invalid changeset fromprepare_changeset
- [Ecto.Changeset] Support multiple fields in
unique_constraint/3
- [Ecto] Add
Ecto.embedded_load/3
andEcto.embedded_dump/2
- [Ecto.Query] Improve error message on invalid JSON expressions
- [Ecto.Repo] Emit
[:ecto, :repo, :init]
telemetry event upon Repo init
- [Ecto.Query] Do not support JSON selectors on
type/2
- [Ecto.Repo] Deprecate
conflict_target: {:constraint, _}
. It is a discouraged approach and{:unsafe_fragment, _}
is still available if someone definitely needs it
v3.4 requires Elixir v1.7+.
- [Ecto.Query] Allow dynamic queries in CTE and improve error message
- [Ecto.Query] Add
Ecto.Query.API.json_extract_path/2
and JSON path support to query syntax. For example,posts.metadata["tags"][0]["name"]
will return the name of the first tag stored in the:map
metadata field - [Ecto.Repo] Add new
default_options/1
callback to repository - [Ecto.Repo] Support passing
:telemetry_options
to repository operations
- [Ecto.Changeset] Properly add validation annotation to
validate_acceptance
- [Ecto.Query] Raise if there is loaded non-empty association data without related key when preloading. This typically means not all fields have been loaded in a query
- [Ecto.Schema] Show meaningful error in case
schema
is invoked twice in anEcto.Schema
- [mix ecto] Do not rely on map ordering when parsing repos
- [mix ecto.gen.repo] Improve error message when a repo is not given
- [Ecto.Query] Support fragments in
lock
- [Ecto.Query] Handle
nil
inselect_merge
with similar semantics to SQL databases (i.e. it simply returnsnil
itself)
- [Ecto.Changeset] Only bump optimistic lock in case of success
- [Ecto.Query] Allow macros in Ecto window expressions
- [Ecto.Schema] Support
:join_defaults
onmany_to_many
associations - [Ecto.Schema] Allow MFargs to be given to association
:defaults
- [Ecto.Type] Add
Ecto.Type.embedded_load
andEcto.Type.embedded_dump
- [Ecto.Repo] Ignore empty hostname when parsing database url (Elixir v1.10 support)
- [Ecto.Repo] Rewrite combinations on Repo.exists? queries
- [Ecto.Schema] Respect child
@schema_prefix
incast_assoc
- [mix ecto.gen.repo] Use
config_path
when writing new config inmix ecto.gen.repo
- [Ecto.Query.WindowAPI] Support
filter/2
- [Ecto.Query.API] Fix
coalesce/2
usage with mixed types
- [Ecto.Adapter] Add
storage_status/1
callback toEcto.Adapters.Storage
behaviour - [Ecto.Changeset] Add
Ecto.Changeset.apply_action!/2
- [Ecto.Changeset] Remove actions restriction in
Ecto.Changeset.apply_action/2
- [Ecto.Repo] Introduce
c:Ecto.Repo.aggregate/2
- [Ecto.Repo] Support
{:replace_all_except, fields}
in:on_conflict
- [Ecto.Query] Make sure the
:prefix
option in:from
/:join
also cascades to subqueries - [Ecto.Query] Make sure the
:prefix
option in:join
also cascades to queries - [Ecto.Query] Use database returned values for literals. Previous Ecto versions knew literals from queries should not be discarded for combinations but, even if they were not discarded, we would ignore the values returned by the database
- [Ecto.Repo] Do not wrap schema operations in a transaction if already inside a transaction. We have also removed the private option called
:skip_transaction
- [Ecto.Repo]
:replace_all_except_primary_keys
is deprecated in favor of{:replace_all_except, fields}
in:on_conflict
- [Ecto.Query] Fix a bug where executing some queries would leak the
{:maybe, ...}
type
- [Ecto.Query] Improve error message on invalid join binding
- [Ecto.Query] Make sure the
:prefix
option in:join
also applies to through associations - [Ecto.Query] Invoke custom type when loading aggregations from the database (but fallback to database value if it can't be cast)
- [mix ecto.gen.repo] Support Elixir v1.9 style configs
- [Ecto.Changeset] Do not convert enums given to
validate_inclusion
to a list
- [Ecto.Changeset] Improve error message on non-atom keys to change/put_change
- [Ecto.Changeset] Allow :with to be given as a
{module, function, args}
tuple oncast_association/cast_embed
- [Ecto.Changeset] Add
fetch_change!/2
andfetch_field!/2
- [Ecto.Query] Fix keyword arguments given to
:on
when a bind is not given to join - [Ecto.Repo] Make sure a preload given to an already preloaded has_many :through is loaded
- [Ecto.Changeset] Add rollover logic for default incremeter in
optimistic_lock
- [Ecto.Query] Also expand macros when used inside
type/2
- [Ecto.Query] Ensure queries with non-cacheable queries in CTEs/combinations are also not-cacheable
v3.2 requires Elixir v1.6+.
- [Ecto.Query] Add common table expressions support
with_cte/3
andrecursive_ctes/2
- [Ecto.Query] Allow
dynamic/3
to be used inorder_by
,distinct
,group_by
, as well as inpartition_by
,order_by
, andframe
insidewindows
- [Ecto.Query] Allow filters in
type/2
expressions - [Ecto.Repo] Merge options given to the repository into the changeset
repo_opts
and assign it back to make it available down the chain - [Ecto.Repo] Add
prepare_query/3
callback that is invoked before query operations - [Ecto.Repo] Support
:returning
option inEcto.Repo.update/2
- [Ecto.Repo] Support passing a one arity function to
Ecto.Repo.transaction/2
, where the argument is the current repo - [Ecto.Type] Add a new
embed_as/1
callback toEcto.Type
that allows adapters to control embedding behaviour - [Ecto.Type] Add
use Ecto.Type
for convenience that implements the new required callbacks
- [Ecto.Association] Ensure we delete an association before inserting when replacing on
has_one
- [Ecto.Query] Do not allow interpolated
nil
in literal keyword list when building query - [Ecto.Query] Do not remove literals from combinations, otherwise UNION/INTERSECTION queries may not match the nummber of values in
select
- [Ecto.Query] Do not attempt to merge at compile-time non-keyword lists given to
select_merge
- [Ecto.Repo] Do not override
:through
associations on preload unless forcing - [Ecto.Repo] Make sure prefix option cascades to combinations and recursive queries
- [Ecto.Schema] Use OS time without drift when generating timestamps
- [Ecto.Type] Allow any datetime in
datetime_add
- [Ecto.Changeset] Make sure
put_assoc
with empty changeset propagates on insert
- [Ecto.Repo] Add
:read_only
repositories - [Ecto.Schema] Also validate options given to
:through
associations
- [Ecto.Changeset] Do not mark
put_assoc
from[]
to[]
or fromnil
tonil
as change - [Ecto.Query] Remove named binding when exluding joins
- [mix ecto.gen.repo] Use
:config_path
instead of hardcoding toconfig/config.exs
- [Ecto.Repo] Allow
:default_dynamic_repo
option onuse Ecto.Repo
- [Ecto.Schema] Support
{:fragment, ...}
in the:where
option for associations
- [Ecto.Query] Fix handling of literals in combinators (union, except, intersection)
- [Ecto.Changeset] Convert validation enums to lists before adding them as validation metadata
- [Ecto.Schema] Properly propragate prefix to join_through source in many_to_many associations
- [Ecto.Changeset] Expose the enum that was validated against in errors from enum-based validations
- [Ecto.Query] Add support for
type+over
- [Ecto.Schema] Allow schema fields to be excluded from queries
- [Ecto.Changeset] Do not list a field as changed if it is updated to its original value
- [Ecto.Query] Keep literal numbers and bitstring in subqueries and unions
- [Ecto.Query] Improve error message for invalid
type/2
expression - [Ecto.Query] Properly count interpolations in
select_merge/2
- [Ecto] Do not require Jason (i.e. it should continue to be an optional dependency)
- [Ecto.Repo] Make sure
many_to_many
andEcto.Multi
work with dynamic repos
v3.1 requires Elixir v1.5+.
- [Ecto.Changeset] Add
not_equal_to
option forvalidate_number
- [Ecto.Query] Improve error message for missing
fragment
arguments - [Ecto.Query] Improve error message on missing struct key for structs built in
select
- [Ecto.Query] Allow dynamic named bindings
- [Ecto.Repo] Add dynamic repository support with
Ecto.Repo.put_dynamic_repo/1
andEcto.Repo.get_dynamic_repo/0
(experimental) - [Ecto.Type] Cast naive_datetime/utc_datetime strings without seconds
- [Ecto.Changeset] Do not run
unsafe_validate_unique
query unless relevant fields were changed - [Ecto.Changeset] Raise if an unknown field is given on
Ecto.Changeset.change/2
- [Ecto.Changeset] Expose the type that was validated in errors generated by
validate_length/3
- [Ecto.Query] Add support for
field/2
as first element oftype/2
and alias as second element oftype/2
- [Ecto.Query] Do not attempt to assert types of named bindings that are not known at compile time
- [Ecto.Query] Properly cast boolean expressions in select
- [Mix.Ecto] Load applications during repo lookup so their app environment is available
- [Ecto.LogEntry] Fully deprecate previously soft deprecated API
- [Ecto.Query]
reverse_order
reverses by primary key if no order is given
- [Ecto.Query] Add
reverse_order/1
- [Ecto.Multi] Raise better error message on accidental rollback inside
Ecto.Multi
- [Ecto.Query] Properly merge deeply nested preloaded joins
- [Ecto.Query] Raise better error message on missing select on schemaless queries
- [Ecto.Schema] Fix parameter ordering in assoc
:where
- [Ecto.Schema] The
:where
option added in Ecto 3.0.0 had a major flaw and it has been reworked in this version. This means a tuple of three elements can no longer be passed to:where
, instead a keyword list must be given. Check the "Filtering associations" section inhas_many/3
docs for more information
- [Ecto.Query] Do not raise on lists of tuples that are not keywords. Instead, let custom Ecto.Type handle them
- [Ecto.Query] Allow
prefix: nil
to be given to subqueries - [Ecto.Query] Use different cache keys for unions/intersections/excepts
- [Ecto.Repo] Fix support for upserts with
:replace
without a schema - [Ecto.Type] Do not lose precision when casting
utc_datetime_usec
with a time zone different than Etc/UTC
- [Decimal] Bump decimal dependency
- [Ecto.Repo] Remove unused
:pool_timeout
- [Ecto.Changeset] Add
count: :bytes
option invalidate_length/3
- [Ecto.Query] Support passing
Ecto.Query
inEcto.Repo.insert_all
- [Ecto.Type] Respect adapter types when loading/dumping arrays and maps
- [Ecto.Query] Ensure no bindings in order_by when using combinations in
Ecto.Query
- [Ecto.Repo] Ensure adapter is compiled (instead of only loaded) before invoking it
- [Ecto.Repo] Support new style child spec from adapters
- [Ecto.LogEntry] Bring old Ecto.LogEntry APIs back for compatibility
- [Ecto.Repo] Consider non-joined fields when merging preloaded assocs only at root
- [Ecto.Repo] Take field sources into account in :replace_all_fields upsert option
- [Ecto.Type] Convert
:utc_datetime
toDateTime
when sending it to adapters
- [Ecto.Query] Ensure parameter order is preserved when using more than 32 parameters
- [Ecto.Query] Consider query prefix when planning association joins
- [Ecto.Repo] Consider non-joined fields as unique parameters when merging preloaded query assocs
Note this version includes changes from ecto
and ecto_sql
but in future releases all ecto_sql
entries will be listed in their own CHANGELOG.
- [Ecto.Adapters.MySQL] Add ability to specify cli_protocol for
ecto.create
andecto.drop
commands - [Ecto.Adapters.PostgreSQL] Add ability to specify maintenance database name for PostgreSQL adapter for
ecto.create
andecto.drop
commands - [Ecto.Changeset] Store constraint name in error metadata for constraints
- [Ecto.Changeset] Add
validations/1
andconstraints/1
instead of allowing direct access on the struct fields - [Ecto.Changeset] Add
:force_update
option when casting relations, to force an update even if there are no changes - [Ecto.Migration] Migrations now lock the migrations table in order to avoid concurrent migrations in a cluster. The type of lock can be configured via the
:migration_lock
repository configuration and defaults to "FOR UPDATE" or disabled if set to nil - [Ecto.Migration] Add
:migration_default_prefix
repository configuration - [Ecto.Migration] Add reversible version of
remove/2
subcommand - [Ecto.Migration] Add support for non-empty arrays as defaults in migrations
- [Ecto.Migration] Add support for logging notices/alerts/warnings when running migrations (only supported by Postgres currently)
- [Ecto.Migrator] Warn when migrating and there is a higher version already migrated in the database
- [Ecto.Multi] Add support for anonymous functions in
insert/4
,update/4
,insert_or_update/4
, anddelete/4
- [Ecto.Query] Support tuples in
where
andhaving
, allowing queries such aswhere: {p.foo, p.bar} > {^foo, ^bar}
- [Ecto.Query] Support arithmetic operators in queries as a thin layer around the DB functionality
- [Ecto.Query] Allow joins in queries to be named via
:as
and allow named bindings - [Ecto.Query] Support excluding specific join types in
exclude/2
- [Ecto.Query] Allow virtual field update in subqueries
- [Ecto.Query] Support
coalesce/2
in queries, such asselect: coalesce(p.title, p.old_title)
- [Ecto.Query] Support
filter/2
in queries, such asselect: filter(count(p.id), p.public == true)
- [Ecto.Query] The
:prefix
and:hints
options are now supported on bothfrom
andjoin
expressions - [Ecto.Query] Support
:asc_nulls_last
,:asc_nulls_first
,:desc_nulls_last
, and:desc_nulls_first
inorder_by
- [Ecto.Query] Allow variables (sources) to be given in queries, for example, useful for invoking functions, such as
fragment("some_function(?)", p)
- [Ecto.Query] Add support for
union
,union_all
,intersection
,intersection_all
,except
andexcept_all
- [Ecto.Query] Add support for
windows
andover
- [Ecto.Query] Raise when comparing a string with a charlist during planning
- [Ecto.Repo] Only start transactions if an association or embed has changed, this reduces the overhead during repository operations
- [Ecto.Repo] Support
:replace_all_except_primary_key
as:on_conflict
strategy - [Ecto.Repo] Support
{:replace, fields}
as:on_conflict
strategy - [Ecto.Repo] Support
:unsafe_fragment
as:conflict_target
- [Ecto.Repo] Support
select
in queries given toupdate_all
anddelete_all
- [Ecto.Repo] Add
Repo.exists?/2
- [Ecto.Repo] Add
Repo.checkout/2
- useful when performing multiple operations in short-time to interval, allowing the pool to be bypassed - [Ecto.Repo] Add
:stale_error_field
toRepo.insert/update/delete
that convertsEcto.StaleEntryError
into a changeset error. The message can also be set with:stale_error_message
- [Ecto.Repo] Preloading now only sorts results by the relationship key instead of sorting by the whole struct
- [Ecto.Schema] Allow
:where
option to be given tohas_many
/has_one
/belongs_to
/many_to_many
- [Ecto.Inspect] Do not fail when inspecting query expressions which have a number of bindings more than bindings available
- [Ecto.Migration] Keep double underscores on autogenerated index names to be consistent with changesets
- [Ecto.Query] Fix
Ecto.Query.API.map/2
for single nil column with join - [Ecto.Migration] Ensure
create_if_not_exists
is properly reversible - [Ecto.Repo] Allow many_to_many associations to be preloaded via a function (before the behaviour was erratic)
- [Ecto.Schema] Make autogen ID loading work with custom type
- [Ecto.Schema] Make
updated_at
have the same value asinserted_at
- [Ecto.Schema] Ensure all fields are replaced with
on_conflict: :replace_all/:replace_all_except_primary_key
and not only the fields sent as changes - [Ecto.Type] Return
:error
when casting NaN or infinite decimals - [mix ecto.migrate] Properly run migrations after ECTO_EDITOR changes
- [mix ecto.migrations] List migrated versions even if the migration file is deleted
- [mix ecto.load] The task now fails on SQL errors on Postgres
Although Ecto 3.0 is a major bump version, the functionality below emits deprecation warnings to ease the migration process. The functionality below will be removed in future Ecto 3.1+ releases.
- [Ecto.Changeset] Passing a list of binaries to
cast/3
is deprecated, please pass a list of atoms instead - [Ecto.Multi]
Ecto.Multi.run/3
now receives the repo in which the transaction is executing as the first argument to functions, and the changes so far as the second argument - [Ecto.Query]
join/5
now expectson: expr
as last argument instead of simplyexpr
. This was done in order to properly support the:as
,:hints
and:prefix
options - [Ecto.Repo] The
:returning
option forupdate_all
anddelete_all
has been deprecated as those statements now supportselect
clauses - [Ecto.Repo] Passing
:adapter
via config is deprecated in favor of passing it onuse Ecto.Repo
- [Ecto.Repo] The
:loggers
configuration is deprecated in favor of "Telemetry Events"
- [Ecto.DateTime]
Ecto.Date
,Ecto.Time
andEcto.DateTime
were previously deprecated and have now been removed - [Ecto.DataType]
Ecto.DataType
protocol has been removed - [Ecto.Migration] Automatically inferred index names may differ in Ecto v3.0 for indexes on complex column names
- [Ecto.Multi]
Ecto.Multi.run/5
now receives the repo in which the transaction is executing as the first argument to functions, and the changes so far as the second argument - [Ecto.Query] A
join
no longer wrapsfragment
in parentheses. In some cases, such as common table expressions, you will have to explicitly wrap the fragment in parens. - [Ecto.Repo] The
on_conflict: :replace_all
option now will also send fields with default values to the database. If you prefer the old behaviour that only sends the changes in the changeset, you can set it toon_conflict: {:replace, Map.keys(changeset.changes)}
(this change is also listed as a bug fix) - [Ecto.Repo] The repository operations are no longer called from association callbacks - this behaviour was not guaranteed in previous versions but we are listing as backwards incompatible changes to help with users relying on this behaviour
- [Ecto.Repo]
:pool_timeout
is no longer supported in favor of a new queue system described inDBConnection.start_link/2
under "Queue config". For most users, configuring:timeout
is enough, as it now includes both queue and query time - [Ecto.Schema]
:time
,:naive_datetime
and:utc_datetime
no longer keep microseconds information. If you want to keep microseconds, use:time_usec
,:naive_datetime_usec
,:utc_datetime_usec
- [Ecto.Schema] The
@schema_prefix
option now only affects thefrom
/join
of where the schema is used and no longer the whole query - [Ecto.Schema.Metadata] The
source
key no longer returns a tuple of the schema_prefix and the table/collection name. It now returns just the table/collection string. You can now access the schema_prefix via theprefix
key. - [Mix.Ecto]
Mix.Ecto.ensure_started/2
has been removed. However, in Ecto 2.2 theMix.Ecto
module was not considered part of the public API and should not have been used but we are listing this for guidance.
- [Ecto.Adapter] Split
Ecto.Adapter
intoEcto.Adapter.Queryable
andEcto.Adapter.Schema
to provide more granular repository APIs - [Ecto.Adapter] The
:sources
field inquery_meta
now contains three elements tuples with{source, schema, prefix}
in order to supportfrom
/join
prefixes (#2572) - [Ecto.Adapter] The database types
time
,utc_datetime
andnaive_datetime
should translate to types with seconds precision while the database typestime_usec
,utc_datetime_usec
andnaive_datetime_usec
should have microseconds precision (#2291) - [Ecto.Adapter] The
on_conflict
argument forinsert
andinsert_all
no longer receives a{:replace_all, list(), atom()}
tuple. Instead, it receives a{fields :: [atom()], list(), atom()}
wherefields
is a list of atoms of the fields to be replaced (#2181) - [Ecto.Adapter]
insert
/update
/delete
now receive both:source
and:prefix
fields instead of a single:source
field with bothsource
andprefix
in it (#2490) - [Ecto.Adapter.Migration] A new
lock_for_migration/4
callback has been added. It is implemented by default byEcto.Adapters.SQL
(#2215) - [Ecto.Adapter.Migration] The
execute_ddl
should now return{:ok, []}
to make space for returning notices/hints/warnings in the future (adapters leveragingEcto.Adapters.SQL
do not have to perform any change) - [Ecto.Query] The
from
field inEcto.Query
now returns aEcto.Query.FromExpr
with the:source
field, unifying the behaviour infrom
andjoin
expressions (#2497) - [Ecto.Query] Tuple expressions are now supported in queries. For example,
where: {p.foo, p.bar} > {p.bar, p.baz}
should translate toWHERE (p.foo, p.bar) > (p.bar, p.baz)
in SQL databases. Adapters should be changed to handle{:{}, meta, exprs}
in the query AST (#2344) - [Ecto.Query] Adapters should support the following arithmetic operators in queries
+
,-
,*
and/
(#2400) - [Ecto.Query] Adapters should support
filter/2
in queries, as inselect: filter(count(p.id), p.public == true)
(#2487)
- See the CHANGELOG.md in the v2.2 branch