Repository: elixir-ecto/ecto (elixir)

Issue:
Bad ordering of interpolated expression when running `update_all`
### Environment

* Elixir version (elixir -v): Elixir 1.5.2 (Erlang/OTP 20)
* Database and version (PostgreSQL 9.4, MongoDB 3.2, etc.): MySQL 5.7.16
* Ecto version (mix deps): 2.2.6
* Database adapter and version (mix deps): mariaex 0.8.3
* Operating system: macOS High Sierra version 10.13.1

### Current behavior

When I run an update_all, it doesn't update the specified row. Examining the query that is produced shows that it fails due to sticking the join interpolated value at the end instead of the beginning of the list:

```elixir
user = %User{id: 2, email: "foo@example.com", first_name: "needs", last_name: "changing"}
changeset = User.changeset(user, %{first_name: "José", last_name: "Valim"})
query = 
  from p in "people", join: u in "users",
  on: p.id == u.person_id and u.id == ^changeset.data.id,
  update: [set: ^Map.to_list(changeset.changes)]

Ecto.Adapters.SQL.to_sql(:update_all, Repo, query)
# => {"UPDATE `people` AS p0 INNER JOIN `users` AS u1 ON (p0.`id` = u1.`person_id`) AND (u1.`id` = ?) SET p0.`first_name` = ?, p0.`last_name` = ?",
#    ["José", "Valim", 2]}
```

### Expected behavior

I expect the list of values to use in the query to be in the correct order so that my update will work:
`[2, "José", "Valim"]`


Pull request description:
Use stricter syntax for update_all with joins in MySQL adapter
Closes #2303 

Refs #2332 

This PR addresses the issue #2303 by way of supporting only a strict subset of `JOIN` syntax for `update_all` queries, as suggested in https://github.com/elixir-ecto/ecto/pull/2332#issuecomment-349582570.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e664df41..cb33573c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,7 @@ This is a new major release for Ecto v3.0. Despite the major number, this is a s
 
 In other words, `Ecto.Date`, `Ecto.Time` and `Ecto.DateTime` no longer exist. Instead developers should use `Date`, `Time`, `DateTime` and `NaiveDateTime` that ship as part of Elixir and are the preferred types since Ecto 2.1. Database adapters have also been standarized to work with Elixir types and they no longer return tuples when developers perform raw queries.
 
-(TO BE IMPLEMENTED) To uniformly support microseconds across all databases, the types `:time`, `:naive_datetime`, `:utc_datetime` will now discard any microseconds information. Ecto v3.0 introduces the types `:time_usec`, `:naive_datetime_usec` and `:utc_datetime_usec` as an alternative for those interested in keeping microseconds. If you want to keep microseconds in your migrations and schemas, you need to configure your repository:
+To uniformly support microseconds across all databases, the types `:time`, `:naive_datetime`, `:utc_datetime` will now discard any microseconds information. Ecto v3.0 introduces the types `:time_usec`, `:naive_datetime_usec` and `:utc_datetime_usec` as an alternative for those interested in keeping microseconds. If you want to keep microseconds in your migrations and schemas, you need to configure your repository:
 
     config :my_app, MyApp.Repo,
       migration_timestamps: [type: :naive_datetime_usec]
@@ -27,19 +27,25 @@ And should be rewritten as:
     # For MySQL
     config :mariaex, :json_library, CustomJSONLib
 
-This means Ecto no longer requires Ecto-specific extensions on databases such as Postgres which leads to better integration with 3rd party libraries and faster compilation times.
-Ecto also now requires the json library to implement an `encode_to_iodata!/1` function instead of `encode!` in line
-with the interface required by Postgrex.
+This means Ecto no longer requires Ecto-specific extensions on databases such as Postgres which leads to better integration with 3rd party libraries and faster compilation times. Ecto also now requires the JSON library to implement an `encode_to_iodata!/1` function instead of `encode!` in line with the interface required by Postgrex.
 
 ## v3.0.0-dev (In Progress)
 
 ### Enhancements
 
+  * [Ecto.Changeset] Put constraint name in error metadata for constraints
   * [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`
   * [Ecto.Query] Add `unsafe_fragment` to queries which allow developers to send dynamicly generated fragments to the database that are not checked (and therefore unsafe)
+  * [Ecto.Query] Support tuples in filter expressions, allowing queries such as `where: {p.foo, p.bar} > {^foo, ^bar}`
   * [Ecto.Repo] Allow some query parameters such as `ssl` and `pool_size` to be given in the repository URL
   * [Ecto.Repo] Support `:replace_all_except_primary_key` as `:on_conflict` strategy
 
+### Bug fixes
+
+  * [Ecto.Migration] Keep double underscores on autogenerated index names to be consistent with changesets
+  * [mix ecto.migrations] List migrated versions even if the migration file is deleted
+
 ### Backwards incompatible changes
 
   * [Ecto.Multi] `Ecto.Multi.run/3` and `Ecto.Multi.run/5` now receive the repo in which the transaction is executing as the first argument to functions, and the changes so far as the second argument.
diff --git a/lib/ecto/adapters/mysql/connection.ex b/lib/ecto/adapters/mysql/connection.ex
index 7bc226aa..0618e57a 100644
--- a/lib/ecto/adapters/mysql/connection.ex
+++ b/lib/ecto/adapters/mysql/connection.ex
@@ -102,9 +102,9 @@ if Code.ensure_loaded?(Mariaex) do
         update_fields(:update, query, sources)
       end
 
-      join   = join(query, sources)
+      {join, wheres} = using_join(query, :update_all, sources)
       prefix = prefix || ["UPDATE ", from, " AS ", name, join, " SET "]
-      where  = where(query, sources)
+      where  = where(%{query | wheres: wheres ++ query.wheres}, sources)
 
       [prefix, fields | where]
     end
@@ -246,6 +246,25 @@ if Code.ensure_loaded?(Mariaex) do
       error!(query, "Unknown update operation #{inspect command} for MySQL")
     end
 
+    defp using_join(%Query{joins: []}, _kind, _sources), do: {[], []}
+    defp using_join(%Query{joins: joins} = query, kind, sources) do
+      froms =
+        intersperse_map(joins, ", ", fn
+          %JoinExpr{qual: :inner, ix: ix, source: source} ->
+            {join, name} = get_source(query, sources, ix, source)
+            [join, " AS " | name]
+          %JoinExpr{qual: qual} ->
+            error!(query, "MySQL adapter supports only inner joins on #{kind}, got: `#{qual}`")
+        end)
+
+      wheres =
+        for %JoinExpr{on: %QueryExpr{expr: value} = expr} <- joins,
+            value != true,
+            do: expr |> Map.put(:__struct__, BooleanExpr) |> Map.put(:op, :and)
+
+      {[?,, ?\s | froms], wheres}
+    end
+
     defp join(%Query{joins: []}, _sources), do: []
     defp join(%Query{joins: joins} = query, sources) do
       Enum.map(joins, fn
@@ -402,6 +421,10 @@ if Code.ensure_loaded?(Mariaex) do
       error!(query, "ilike is not supported by MySQL")
     end
 
+    defp expr({:{}, _, elems}, sources, query) do
+      [?(, intersperse_map(elems, ?,, &expr(&1, sources, query)), ?)]
+    end
+
     defp expr({fun, _, args}, sources, query) when is_atom(fun) and is_list(args) do
       {modifier, args} =
         case args do
diff --git a/lib/ecto/adapters/postgres/connection.ex b/lib/ecto/adapters/postgres/connection.ex
index 8fb2033f..c703f6e7 100644
--- a/lib/ecto/adapters/postgres/connection.ex
+++ b/lib/ecto/adapters/postgres/connection.ex
@@ -470,6 +470,10 @@ if Code.ensure_loaded?(Postgrex) do
        interval(count, interval, sources, query) | ")::date"]
     end
 
+    defp expr({:{}, _, elems}, sources, query) do
+      [?(, intersperse_map(elems, ?,, &expr(&1, sources, query)), ?)]
+    end
+
     defp expr({fun, _, args}, sources, query) when is_atom(fun) and is_list(args) do
       {modifier, args} =
         case args do
diff --git a/lib/ecto/changeset.ex b/lib/ecto/changeset.ex
index e6aa551b..ac5b9da3 100644
--- a/lib/ecto/changeset.ex
+++ b/lib/ecto/changeset.ex
@@ -123,7 +123,8 @@ defmodule Ecto.Changeset do
       embedded data via parent changeset - an error will be added to the parent
       changeset, and it will be marked as invalid
     * `:nilify` - sets owner reference column to `nil` (available only for
-      associations)
+      associations). Use this on a `belongs_to` column to allow the association
+      to be cleared out so that it can be set to a new value.
     * `:update` - updates the association, available only for `has_one` and `belongs_to`.
       This option will update all the fields given to the changeset including the id
       for the association
@@ -1791,7 +1792,7 @@ defmodule Ecto.Changeset do
           confirmation_missing(opts, error_field)
       end
 
-    %{changeset | validations: [{:confirmation, opts} | changeset.validations],
+    %{changeset | validations: [{field, {:confirmation, opts}} | changeset.validations],
                   errors: errors ++ changeset.errors,
                   valid?: changeset.valid? and errors == []}
   end
diff --git a/lib/ecto/migration.ex b/lib/ecto/migration.ex
index 55824959..d249f350 100644
--- a/lib/ecto/migration.ex
+++ b/lib/ecto/migration.ex
@@ -174,6 +174,11 @@ defmodule Ecto.Migration do
 
           config :app, App.Repo, migration_lock: nil
 
+    * `:migration_default_prefix` - Ecto defaults to `nil` for the database prefix for
+      migrations but you can configure it via:
+
+          config :app, App.Repo, migration_default_prefix: "kewl"
+
   """
 
   defmodule Index do
@@ -925,7 +930,8 @@ defmodule Ecto.Migration do
 
     cond do
       is_nil(prefix) ->
-        %{index_or_table | prefix: runner_prefix}
+        prefix = runner_prefix || Runner.repo_config(:migration_default_prefix, nil)
+        %{index_or_table | prefix: prefix}
       is_nil(runner_prefix) or runner_prefix == to_string(prefix) ->
         index_or_table
       true ->
diff --git a/lib/ecto/migrator.ex b/lib/ecto/migrator.ex
index a18925f1..18ed1416 100644
--- a/lib/ecto/migrator.ex
+++ b/lib/ecto/migrator.ex
@@ -209,15 +209,10 @@ defmodule Ecto.Migrator do
 
   """
   def migrations(repo, directory) do
-    versions = migrated_versions(repo)
-
-    Enum.map(pending_in_direction(versions, directory, :down) |> Enum.reverse, fn {a, b, _}
-      -> {:up, a, b}
-    end)
-    ++
-    Enum.map(pending_in_direction(versions, directory, :up), fn {a, b, _} ->
-      {:down, a, b}
-    end)
+    repo
+    |> migrated_versions
+    |> collect_migrations(directory)
+    |> Enum.sort_by(fn {_, version, _} -> version end)
   end
 
   defp lock_for_migrations(repo, opts, fun) do
@@ -268,6 +263,34 @@ defmodule Ecto.Migrator do
     |> Enum.reverse
   end
 
+  defp collect_migrations(versions, migration_source) do
+    ups_with_file =
+      versions
+      |> pending_in_direction(migration_source, :down)
+      |> Enum.map(fn {version, name, _} -> {:up, version, name} end)
+
+    ups_without_file =
+      versions
+      |> versions_without_file(migration_source)
+      |> Enum.map(fn version -> {:up, version, "** FILE NOT FOUND **"} end)
+
+    downs =
+      versions
+      |> pending_in_direction(migration_source, :up)
+      |> Enum.map(fn {version, name, _} -> {:down, version, name} end)
+
+    ups_with_file ++ ups_without_file ++ downs
+  end
+
+  defp versions_without_file(versions, migration_source) do
+    versions_with_file =
+      migration_source
+      |> migrations_for
+      |> Enum.map(&elem(&1, 0))
+
+    versions -- versions_with_file
+  end
+
   # This function will match directories passed into `Migrator.run`.
   defp migrations_for(migration_source) when is_binary(migration_source) do
     query = Path.join(migration_source, "*")
diff --git a/lib/ecto/query/builder.ex b/lib/ecto/query/builder.ex
index c4e70728..669d46dd 100644
--- a/lib/ecto/query/builder.ex
+++ b/lib/ecto/query/builder.ex
@@ -24,7 +24,7 @@ defmodule Ecto.Query.Builder do
   map.
   """
   @spec escape(Macro.t, quoted_type, {map, term}, Keyword.t,
-               Macro.Env.t | {Macro.Env.t, fun}) :: {Macro.t, %{}}
+               Macro.Env.t | {Macro.Env.t, fun}) :: {Macro.t, {map, term}}
   def escape(expr, type, params_acc, vars, env)
 
   # var.x - where var is bound
@@ -240,6 +240,32 @@ defmodule Ecto.Query.Builder do
            "Instead use boolean operators: `and`, `or`, and `not`"
   end
 
+  # Tuple
+  def escape({left, right}, type, params_acc, vars, env) do
+    escape({:{}, [], [left, right]}, type, params_acc, vars, env)
+  end
+
+  # Tuple
+  def escape({:{}, _, list}, {:tuple, types}, params_acc, vars, env) do
+    if Enum.count(list) == Enum.count(types) do
+      {list, params_acc} =
+        list
+        |> Enum.zip(types)
+        |> Enum.map_reduce(params_acc, fn {expr, type}, params_acc ->
+             escape(expr, type, params_acc, vars, env)
+           end)
+      expr = {:{}, [], [:{}, [], list]}
+      {expr, params_acc}
+    else
+      escape({:{}, [], list}, :any, params_acc, vars, env)
+    end
+  end
+
+  # Tuple
+  def escape({:{}, _, _}, _, _, _, _) do
+    error! "Tuples can only be used in comparisons with literal tuples of the same size"
+  end
+
   # Other functions - no type casting
   def escape({name, _, args} = expr, type, params_acc, vars, env) when is_atom(name) and is_list(args) do
     case call_type(name, length(args)) do
@@ -677,6 +703,10 @@ defmodule Ecto.Query.Builder do
   def quoted_type(literal, _vars) when is_boolean(literal), do: :boolean
   def quoted_type(literal, _vars) when is_integer(literal), do: :integer
 
+  # Tuples
+  def quoted_type({left, right}, vars), do: quoted_type({:{}, [], [left, right]}, vars)
+  def quoted_type({:{}, _, elems}, vars), do: {:tuple, Enum.map(elems, &quoted_type(&1, vars))}
+
   def quoted_type({name, _, args}, _vars) when is_atom(name) and is_list(args) do
     case call_type(name, length(args)) do
       {_in, out} -> out
diff --git a/lib/ecto/query/planner.ex b/lib/ecto/query/planner.ex
index 73933aec..65a866db 100644
--- a/lib/ecto/query/planner.ex
+++ b/lib/ecto/query/planner.ex
@@ -61,6 +61,8 @@ defmodule Ecto.Query.Planner do
   def rewrite_sources(%{expr: expr, params: params} = part, mapping) do
     expr =
       Macro.prewalk expr, fn
+        %Ecto.Query.Tagged{type: type, tag: tag} = tagged ->
+          %{tagged | type: rewrite_type(type, mapping), tag: rewrite_type(tag, mapping)}
         {:&, meta, [ix]} ->
           {:&, meta, [mapping.(ix)]}
         other ->
@@ -69,10 +71,8 @@ defmodule Ecto.Query.Planner do
 
     params =
       Enum.map params, fn
-        {val, {composite, {ix, field}}} when is_integer(ix) ->
-          {val, {composite, {mapping.(ix), field}}}
-        {val, {ix, field}} when is_integer(ix) ->
-          {val, {mapping.(ix), field}}
+        {val, type} ->
+          {val, rewrite_type(type, mapping)}
         val ->
           val
       end
@@ -80,6 +80,18 @@ defmodule Ecto.Query.Planner do
     %{part | expr: expr, params: params}
   end
 
+  defp rewrite_type({composite, {ix, field}}, mapping) when is_integer(ix) do
+    {composite, {mapping.(ix), field}}
+  end
+
+  defp rewrite_type({ix, field}, mapping) when is_integer(ix) do
+    {mapping.(ix), field}
+  end
+
+  defp rewrite_type(other, _mapping) do
+    other
+  end
+
   @doc """
   Plans the query for execution.
 
diff --git a/lib/ecto/repo.ex b/lib/ecto/repo.ex
index 55487276..4917af03 100644
--- a/lib/ecto/repo.ex
+++ b/lib/ecto/repo.ex
@@ -345,7 +345,7 @@ defmodule Ecto.Repo do
   @doc """
   Fetches a single result from the query.
 
-  Returns `nil` if no result was found.
+  Returns `nil` if no result was found. Raises if more than one entry.
 
   ## Options
 
@@ -360,6 +360,8 @@ defmodule Ecto.Repo do
 
   @doc """
   Similar to `get_by/3` but raises `Ecto.NoResultsError` if no record was found.
+  
+  Raises if more than one entry.
 
   ## Options
 


Test patch:
diff --git a/integration_test/cases/joins.exs b/integration_test/cases/joins.exs
index 5ac8c99f..d3aef0e7 100644
--- a/integration_test/cases/joins.exs
+++ b/integration_test/cases/joins.exs
@@ -64,11 +64,15 @@ defmodule Ecto.Integration.JoinsTest do
     p2 = TestRepo.insert!(%Post{title: "2"})
     c1 = TestRepo.insert!(%Permalink{url: "1", post_id: p2.id})
 
-    permalink = from c in Permalink, where: c.url == ^"1"
+    # Joined query without parameter
+    permalink = from c in Permalink, where: c.url == "1"
 
     query = from(p in Post, join: c in ^permalink, on: c.post_id == p.id, select: {p, c})
     assert [{^p2, ^c1}] = TestRepo.all(query)
 
+    # Joined query witho parameter
+    permalink = from c in Permalink, where: c.url == "1"
+
     query = from(p in Post, join: c in ^permalink, on: c.id == ^c1.id, order_by: p.title, select: {p, c})
     assert [{^p1, ^c1}, {^p2, ^c1}] = TestRepo.all(query)
   end
diff --git a/test/ecto/adapters/mysql_test.exs b/test/ecto/adapters/mysql_test.exs
index 91468c84..e9ba8ebf 100644
--- a/test/ecto/adapters/mysql_test.exs
+++ b/test/ecto/adapters/mysql_test.exs
@@ -128,6 +128,9 @@ defmodule Ecto.Adapters.MySQLTest do
   test "where" do
     query = Schema |> where([r], r.x == 42) |> where([r], r.y != 43) |> select([r], r.x) |> normalize
     assert all(query) == ~s{SELECT s0.`x` FROM `schema` AS s0 WHERE (s0.`x` = 42) AND (s0.`y` != 43)}
+
+    query = Schema |> where([r], {r.x, r.y} > {1, 2}) |> select([r], r.x) |> normalize
+    assert all(query) == ~s{SELECT s0.`x` FROM `schema` AS s0 WHERE ((s0.`x`,s0.`y`) > (1,2))}
   end
 
   test "or_where" do
@@ -355,13 +358,13 @@ defmodule Ecto.Adapters.MySQLTest do
     query = Schema |> join(:inner, [p], q in Schema2, p.x == q.z)
                   |> update([_], set: [x: 0]) |> normalize(:update_all)
     assert update_all(query) ==
-           ~s{UPDATE `schema` AS s0 INNER JOIN `schema2` AS s1 ON s0.`x` = s1.`z` SET s0.`x` = 0}
+           ~s{UPDATE `schema` AS s0, `schema2` AS s1 SET s0.`x` = 0 WHERE (s0.`x` = s1.`z`)}
 
     query = from(e in Schema, where: e.x == 123, update: [set: [x: 0]],
                              join: q in Schema2, on: e.x == q.z) |> normalize(:update_all)
     assert update_all(query) ==
-           ~s{UPDATE `schema` AS s0 INNER JOIN `schema2` AS s1 ON s0.`x` = s1.`z` } <>
-           ~s{SET s0.`x` = 0 WHERE (s0.`x` = 123)}
+           ~s{UPDATE `schema` AS s0, `schema2` AS s1 } <>
+           ~s{SET s0.`x` = 0 WHERE (s0.`x` = s1.`z`) AND (s0.`x` = 123)}
   end
 
   test "update all with prefix" do
diff --git a/test/ecto/adapters/postgres_test.exs b/test/ecto/adapters/postgres_test.exs
index 780eed3d..5cc1ea0f 100644
--- a/test/ecto/adapters/postgres_test.exs
+++ b/test/ecto/adapters/postgres_test.exs
@@ -150,6 +150,9 @@ defmodule Ecto.Adapters.PostgresTest do
   test "where" do
     query = Schema |> where([r], r.x == 42) |> where([r], r.y != 43) |> select([r], r.x) |> normalize
     assert all(query) == ~s{SELECT s0."x" FROM "schema" AS s0 WHERE (s0."x" = 42) AND (s0."y" != 43)}
+
+    query = Schema |> where([r], {r.x, r.y} > {1, 2}) |> select([r], r.x) |> normalize
+    assert all(query) == ~s{SELECT s0."x" FROM "schema" AS s0 WHERE ((s0."x",s0."y") > (1,2))}
   end
 
   test "or_where" do
diff --git a/test/ecto/changeset_test.exs b/test/ecto/changeset_test.exs
index a340663f..a51c5fd9 100644
--- a/test/ecto/changeset_test.exs
+++ b/test/ecto/changeset_test.exs
@@ -971,48 +971,57 @@ defmodule Ecto.ChangesetTest do
                 |> validate_confirmation(:title)
     assert changeset.valid?
     assert changeset.errors == []
+    assert changeset.validations == [{:title, {:confirmation, []}}]
 
     changeset = changeset(%{"title" => "title"})
                 |> validate_confirmation(:title)
     assert changeset.valid?
     assert changeset.errors == []
+    assert changeset.validations == [{:title, {:confirmation, []}}]
 
     changeset = changeset(%{"title" => "title"})
                 |> validate_confirmation(:title, required: false)
     assert changeset.valid?
     assert changeset.errors == []
+    assert changeset.validations == [{:title, {:confirmation, [required: false]}}]
 
     changeset = changeset(%{"title" => "title"})
                 |> validate_confirmation(:title, required: true)
     refute changeset.valid?
     assert changeset.errors == [title_confirmation: {"can't be blank", [validation: :required]}]
+    assert changeset.validations == [{:title, {:confirmation, [required: true]}}]
 
     changeset = changeset(%{"title" => "title", "title_confirmation" => nil})
                 |> validate_confirmation(:title)
     refute changeset.valid?
     assert changeset.errors == [title_confirmation: {"does not match confirmation", [validation: :confirmation]}]
+    assert changeset.validations == [{:title, {:confirmation, []}}]
 
     changeset = changeset(%{"title" => "title", "title_confirmation" => "not title"})
                 |> validate_confirmation(:title)
     refute changeset.valid?
     assert changeset.errors == [title_confirmation: {"does not match confirmation", [validation: :confirmation]}]
+    assert changeset.validations == [{:title, {:confirmation, []}}]
 
     changeset = changeset(%{"title" => "title", "title_confirmation" => "not title"})
                 |> validate_confirmation(:title, message: "doesn't match field below")
     refute changeset.valid?
     assert changeset.errors == [title_confirmation: {"doesn't match field below", [validation: :confirmation]}]
+    assert changeset.validations == [{:title, {:confirmation, [message: "doesn't match field below"]}}]
 
     # Skip when no parameter
     changeset = changeset(%{"title" => "title"})
                 |> validate_confirmation(:title, message: "password doesn't match")
     assert changeset.valid?
     assert changeset.errors == []
+    assert changeset.validations == [{:title, {:confirmation, [message: "password doesn't match"]}}]
 
     # With casting
     changeset = changeset(%{"upvotes" => "1", "upvotes_confirmation" => "1"})
                 |> validate_confirmation(:upvotes)
     assert changeset.valid?
     assert changeset.errors == []
+    assert changeset.validations == [{:upvotes, {:confirmation, []}}]
 
     # With blank change
     changeset = changeset(%{"password" => "", "password_confirmation" => "password"})
@@ -1031,6 +1040,7 @@ defmodule Ecto.ChangesetTest do
                 |> validate_confirmation(:password)
     refute changeset.valid?
     assert changeset.errors == []
+    assert changeset.validations == []
   end
 
   test "validate_acceptance/3" do
diff --git a/test/ecto/migration_test.exs b/test/ecto/migration_test.exs
index 67a8257e..ef7b2cb8 100644
--- a/test/ecto/migration_test.exs
+++ b/test/ecto/migration_test.exs
@@ -343,6 +343,33 @@ defmodule Ecto.MigrationTest do
     assert table.prefix == "foo"
   end
 
+  @tag prefix: "foo", repo_config: [migration_default_prefix: "baz"]
+  test "forward: creates a table with prefix from manager overriding the default prefix configuration" do
+    create(table(:posts))
+    flush()
+
+    {_, table, _} = last_command()
+    assert table.prefix == "foo"
+  end
+
+  @tag repo_config: [migration_default_prefix: "baz"]
+  test "forward: creates a table with prefix from migration overriding the default prefix configuration" do
+    create(table(:posts, prefix: "foo"))
+    flush()
+
+    {_, table, _} = last_command()
+    assert table.prefix == "foo"
+  end  
+
+  @tag repo_config: [migration_default_prefix: "baz"]
+  test "forward: create a table with prefix from configuration" do
+    create(table(:posts))
+    flush()
+
+    {_, table, _} = last_command()
+    assert table.prefix == "baz"
+  end
+
   @tag prefix: :foo
   test "forward: creates a table with prefix from manager matching atom prefix" do
     create(table(:posts, prefix: "foo"))
@@ -385,6 +412,14 @@ defmodule Ecto.MigrationTest do
     assert table.prefix == "foo"
   end
 
+  @tag repo_config: [migration_default_prefix: "baz"]
+  test "forward: drops a table with prefix from configuration" do
+    drop(table(:posts))
+    flush()
+    {:drop, table} = last_command()
+    assert table.prefix == "baz"
+  end
+
   test "forward: rename column on table with index prefixed from migration" do
     rename(table(:posts, prefix: "foo"), :given_name, to: :first_name)
     flush()
@@ -404,6 +439,16 @@ defmodule Ecto.MigrationTest do
     assert new_name == :first_name
   end
 
+  @tag repo_config: [migration_default_prefix: "baz"]
+  test "forward: rename column on table with index prefixed from configuration" do
+    rename(table(:posts), :given_name, to: :first_name)
+    flush()
+
+    {_, table, _, new_name} = last_command()
+    assert table.prefix == "baz"
+    assert new_name == :first_name
+  end
+
   test "forward: creates an index with prefix from migration" do
     create index(:posts, [:title], prefix: "foo")
     flush()
@@ -419,6 +464,14 @@ defmodule Ecto.MigrationTest do
     assert index.prefix == "foo"
   end
 
+  @tag repo_config: [migration_default_prefix: "baz"]
+  test "forward: creates an index with prefix from configuration" do
+    create index(:posts, [:title])
+    flush()
+    {_, index} = last_command()
+    assert index.prefix == "baz"
+  end
+
   test "forward: drops an index with a prefix from migration" do
     drop index(:posts, [:title], prefix: "foo")
     flush()
@@ -434,6 +487,14 @@ defmodule Ecto.MigrationTest do
     assert index.prefix == "foo"
   end
 
+  @tag repo_config: [migration_default_prefix: "baz"]
+  test "forward: drops an index with a prefix from configuration" do
+    drop index(:posts, [:title])
+    flush()
+    {_, index} = last_command()
+    assert index.prefix == "baz"
+  end
+
   test "forward: executes a command" do
     execute "SELECT 1", "SELECT 2"
     flush()
diff --git a/test/ecto/migrator_test.exs b/test/ecto/migrator_test.exs
index 0c8e9bb5..4bb72072 100644
--- a/test/ecto/migrator_test.exs
+++ b/test/ecto/migrator_test.exs
@@ -275,7 +275,7 @@ defmodule Ecto.MigratorTest do
     end
   end
 
-  test "version migrations work iside directories" do
+  test "version migrations work inside directories" do
     in_tmp fn path ->
       File.mkdir_p!("foo")
       create_migration "foo/13_version_in_dir.exs"
@@ -303,6 +303,26 @@ defmodule Ecto.MigratorTest do
     end
   end
 
+  test "migrations will give the migration status while file is deleted" do
+    in_tmp fn path ->
+      create_migration "1_up_migration_1.exs"
+      create_migration "2_up_migration_2.exs"
+      create_migration "3_up_migration_3.exs"
+      create_migration "4_down_migration_1.exs"
+
+      File.rm("2_up_migration_2.exs")
+
+      expected_result = [
+        {:up, 1, "up_migration_1"},
+        {:up, 2, "** FILE NOT FOUND **"},
+        {:up, 3, "up_migration_3"},
+        {:down, 4, "down_migration_1"},
+      ]
+
+      assert migrations(TestRepo, path) == expected_result
+    end
+  end
+
   test "migrations run inside a transaction if the adapter supports ddl transactions" do
     capture_log fn ->
       put_test_adapter_config(supports_ddl_transaction?: true, test_process: self())
diff --git a/test/ecto/query/builder/filter_test.exs b/test/ecto/query/builder/filter_test.exs
index 6a944b29..4715a4e3 100644
--- a/test/ecto/query/builder/filter_test.exs
+++ b/test/ecto/query/builder/filter_test.exs
@@ -19,6 +19,10 @@ defmodule Ecto.Query.Builder.FilterTest do
       assert escape(:where, quote do [x: ^"foo", y: ^"bar"] end, 0, [x: 0], __ENV__) ===
              {Macro.escape(quote do &0.x == ^0 and &0.y == ^1 end),
               %{0 => {"foo", {0, :x}}, 1 => {"bar", {0, :y}}}}
+
+      assert escape(:where, quote do {x.x} == {^"foo"} end, 0, [x: 0], __ENV__) ===
+             {Macro.escape(quote do {&0.x} == {^0} end),
+              %{0 => {"foo", {0, :x}}}}
     end
 
     test "raises on invalid expressions" do
@@ -26,6 +30,11 @@ defmodule Ecto.Query.Builder.FilterTest do
                    ~r"expected a keyword list at compile time in where, got: `\[\{1, 2\}\]`", fn ->
         escape(:where, quote do [{1, 2}] end, 0, [], __ENV__)
       end
+
+      assert_raise Ecto.Query.CompileError,
+                   ~r"Tuples can only be used in comparisons with literal tuples of the same size", fn ->
+        escape(:where, quote do {1, 2} > ^foo end, 0, [], __ENV__)
+      end
     end
 
     test "raises on nils" do