Ecto.Adapters.SQL

Behaviour and implementation for SQL adapters.

The implementation for SQL adapter provides a pooled based implementation of SQL and also expose a query function to developers.

Developers that use Ecto.Adapters.SQL should implement the connection module with specifics on how to connect to the database and also how to translate the queries to SQL. See Ecto.Adapters.SQL.Connection for more info.

Source

Summary

begin_test_transaction(repo, opts \\ [])

Starts a transaction for test

query(repo, sql, params, opts \\ [])

Runs custom SQL query on given repo

restart_test_transaction(repo, opts \\ [])

Restarts a test transaction, see begin_test_transaction/2

rollback_test_transaction(repo, opts \\ [])

Ends a test transaction, see begin_test_transaction/2

Functions

begin_test_transaction(repo, opts \\ [])

Specs:

Starts a transaction for test.

This function work by starting a transaction and storing the connection back in the pool with an open transaction. At the end of the test, the transaction must be rolled back with rollback_test_transaction, reverting all data added during tests.

IMPORTANT: Test transactions only work if the connection pool has size of 1 and does not support any overflow.

Example

The first step is to configure your database pool to have size of 1 and no max overflow. You set those options in your config/config.exs:

config :my_app, Repo,
  size: 1,
  max_overflow: 0

Since you don't want those options in your production database, we typically recommend to create a config/test.exs and add the following to the bottom of your config/config.exs file:

import_config "config/#{Mix.env}.exs"

Now with the test database properly configured, you can write transactional tests:

# At the end of your test_helper.exs
# From now, all tests happen inside a transaction
Ecto.Adapters.SQL.begin_test_transaction(TestRepo)

defmodule PostTest do
  # Tests that use the shared repository cannot be async
  use ExUnit.Case

  setup do
    # Go back to a clean slate at the beginning of every test
    Ecto.Adapters.SQL.restart_test_transaction(TestRepo)
    :ok
  end

  test "create comment" do
    assert %Post{} = TestRepo.insert(%Post{})
  end
end

In some cases, you may want to start the test transaction only for specific tests and then roll it back. You can do it as:

defmodule PostTest do
  # Tests that use the shared repository cannot be async
  use ExUnit.Case

  setup_all do
    # Wrap this case in a transaction
    Ecto.Adapters.SQL.begin_test_transaction(TestRepo)

    # Roll it back once we are done
    on_exit fn ->
      Ecto.Adapters.SQL.rollback_test_transaction(TestRepo)
    end

    :ok
  end

  setup do
    # Go back to a clean slate at the beginning of every test
    Ecto.Adapters.SQL.restart_test_transaction(TestRepo)
    :ok
  end

  test "create comment" do
    assert %Post{} = TestRepo.insert(%Post{})
  end
end
Source
query(repo, sql, params, opts \\ [])

Specs:

Runs custom SQL query on given repo.

In case of success, it must return an :ok tuple containing a map with at least two keys:

  • :num_rows - the number of rows affected

  • :rows - the result set as a list. nil may be returned instead of the list if the command does not yield any row as result (but still yields the number of affected rows, like a delete command without returning would)

Options

  • :timeout - The time in milliseconds to wait for the call to finish, :infinity will wait indefinitely (default: 5000)

  • :log - When false, does not log the query

Examples

iex> Ecto.Adapters.SQL.query(MyRepo, "SELECT $1 + $2", [40, 2])
%{rows: [{42}], num_rows: 1}
Source
restart_test_transaction(repo, opts \\ [])

Specs:

Restarts a test transaction, see begin_test_transaction/2.

Source
rollback_test_transaction(repo, opts \\ [])

Specs:

Ends a test transaction, see begin_test_transaction/2.

Source