Skip to content

Commit

Permalink
Add :include option to support covering indexes (#182)
Browse files Browse the repository at this point in the history
When specifying the :include option during index creation, the `CREATE
INDEX` operation receives an additional option `INCLUDE` to specify
non-key columns to include in the index.
  • Loading branch information
jeroenvisser101 committed Jan 29, 2020
1 parent f189802 commit 12a347f
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 2 deletions.
2 changes: 2 additions & 0 deletions lib/ecto/adapters/postgres/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,7 @@ if Code.ensure_loaded?(Postgrex) do

def execute_ddl({:create, %Index{} = index}) do
fields = intersperse_map(index.columns, ", ", &index_expr/1)
include_fields = intersperse_map(index.include, ", ", &index_expr/1)

queries = [["CREATE ",
if_do(index.unique, "UNIQUE "),
Expand All @@ -762,6 +763,7 @@ if Code.ensure_loaded?(Postgrex) do
quote_table(index.prefix, index.table),
if_do(index.using, [" USING " , to_string(index.using)]),
?\s, ?(, fields, ?),
if_do(include_fields != [], [" INCLUDE ", ?(, include_fields, ?)]),
if_do(index.where, [" WHERE ", to_string(index.where)])]]

queries ++ comments_on("INDEX", quote_table(index.prefix, index.name), index.comment)
Expand Down
12 changes: 10 additions & 2 deletions lib/ecto/migration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ defmodule Ecto.Migration do
terminates.
However, in some situations you may want to guarantee that all of the
previous steps have been executed before continuing. This is useful when
you need to apply a set of changes to the table before continuing with the
previous steps have been executed before continuing. This is useful when
you need to apply a set of changes to the table before continuing with the
migration. This can be done with `flush/0`:
def up do
Expand Down Expand Up @@ -315,6 +315,7 @@ defmodule Ecto.Migration do
unique: false,
concurrently: false,
using: nil,
include: [],
where: nil,
comment: nil,
options: nil
Expand All @@ -327,6 +328,7 @@ defmodule Ecto.Migration do
unique: boolean,
concurrently: boolean,
using: atom | String.t,
include: [atom | String.t],
where: atom | String.t,
comment: String.t | nil,
options: String.t
Expand Down Expand Up @@ -625,6 +627,9 @@ defmodule Ecto.Migration do
* `:using` - configures the index type.
* `:prefix` - specify an optional prefix for the index.
* `:where` - specify conditions for a partial index.
* `:include` - specify fields for a covering index. This is not supported
by all databases. For more information on PostgreSQL support, please
[read the official docs](https://www.postgresql.org/docs/11/indexes-index-only-scans.html).
## Adding/dropping indexes concurrently
Expand Down Expand Up @@ -696,6 +701,9 @@ defmodule Ecto.Migration do
# Partial indexes are created by specifying a :where option
create index("products", [:user_id], where: "price = 0", name: :free_products_index)
# Covering indexes are created by specifying a :include option
create index("products", [:user_id], include: [:category_id])
Indexes also support custom expressions. Some databases may require the
index expression to be written between parentheses:
Expand Down
10 changes: 10 additions & 0 deletions test/ecto/adapters/postgres_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1532,6 +1532,16 @@ defmodule Ecto.Adapters.PostgresTest do
[~s|CREATE UNIQUE INDEX "posts_permalink_index" ON "posts" ("permalink") WHERE public|]
end

test "create index with include fields" do
create = {:create, index(:posts, [:permalink], unique: true, include: [:public])}
assert execute_ddl(create) ==
[~s|CREATE UNIQUE INDEX "posts_permalink_index" ON "posts" ("permalink") INCLUDE ("public")|]

create = {:create, index(:posts, [:permalink], unique: true, include: [:public], where: "public IS TRUE")}
assert execute_ddl(create) ==
[~s|CREATE UNIQUE INDEX "posts_permalink_index" ON "posts" ("permalink") INCLUDE ("public") WHERE public IS TRUE|]
end

test "create index concurrently" do
index = index(:posts, [:permalink])
create = {:create, %{index | concurrently: true}}
Expand Down

0 comments on commit 12a347f

Please sign in to comment.