Skip to content

Ecto.Adapters.SQL.stream/2 does not consider dynamic repo #701

@frerich

Description

@frerich

Elixir version

1.19.4

Database and Version

PostgreSQL 17.7

Ecto Versions

ecto 3.13.5, ecto_sql 3.13.2

Database Adapter and Versions (postgrex, myxql, etc)

postgrex 0.21.1

Current behavior

This small script attempts to stream a single line of text into a database table while using a dynamic repo:

Mix.install([
  {:ecto_sql, "~> 3.10"},
  {:postgrex, ">= 0.0.0"}
])

Application.put_env(:myapp, Repo, database: "example_db")

defmodule Repo do
  use Ecto.Repo, adapter: Ecto.Adapters.Postgres, otp_app: :myapp
end

# Bring up the Repo
Repo.__adapter__().storage_up(Repo.config())
{:ok, _pid} = Repo.start_link(Repo.config())

# Now, the actual test case.
default_dyn_repo  = Repo.get_dynamic_repo()

{:ok, dyn_repo} = Repo.config() |> Keyword.put(:name, nil) |> Repo.start_link()

Repo.put_dynamic_repo(dyn_repo)

Repo.transaction(fn ->
  ["data"]
  |> Stream.into(Ecto.Adapters.SQL.stream(Repo, "COPY tmp_table FROM STDIN"))
  |> Stream.run()
end)

Repo.put_dynamic_repo(default_dyn_repo)

It currently fails with

** (RuntimeError) cannot collect into stream outside of transaction
    (ecto_sql 3.13.3) lib/ecto/adapters/sql.ex:1152: Ecto.Adapters.SQL.into/4
    (ecto_sql 3.13.3) lib/ecto/adapters/sql/stream.ex:29: Collectable.Ecto.Adapters.SQL.Stream.into/1
    (elixir 1.19.4) lib/stream.ex:589: Stream.do_into/5
    (elixir 1.19.4) lib/stream.ex:707: Stream.run/1
    repo.exs:9: anonymous fn/1 in Repo.transaction/2
    (ecto 3.13.5) lib/ecto/repo/transaction.ex:11: anonymous fn/3 in Ecto.Repo.Transaction.transact/4
    (ecto_sql 3.13.3) lib/ecto/adapters/sql.ex:1468: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
    (db_connection 2.8.1) lib/db_connection.ex:1753: DBConnection.run_transaction/4

To fix this, I currently have to pass dyn_repo (the PID of the dynamic repo) resp. Repo.get_dynamic_repo() to Ecto.Adapters.SQL.stream/2 explicitly, instead of the module Repo. It is unclear if the desired (if so: undocumented) requirement, or if it's a missing feature in Ecto.Adapters.SQL.stream/2 to call get_dynamic_repo/0 itself.

Expected behavior

The Ecto.Adapters.SQL.stream/2 call should respect the dynamic repo. I.e. the example program should notice that there is an active transaction and start streaming, which should then run into the error

** (Postgrex.Error) ERROR 42P01 (undefined_table) relation "tmp_table" does not exist
    (db_connection 2.8.1) lib/db_connection.ex:848: DBConnection.execute!/4
    (postgrex 0.21.1) lib/postgrex/stream.ex:85: anonymous fn/7 in Collectable.Postgrex.Stream.make_into/4
    (ecto_sql 3.13.3) lib/ecto/adapters/sql/stream.ex:36: anonymous fn/4 in Collectable.Ecto.Adapters.SQL.Stream.make_into/2
    (elixir 1.19.4) lib/stream.ex:612: Stream.do_into/4
    (elixir 1.19.4) lib/stream.ex:707: Stream.run/1
    repo.exs:9: anonymous fn/1 in Repo.transaction/2
    (ecto 3.13.5) lib/ecto/repo/transaction.ex:11: anonymous fn/3 in Ecto.Repo.Transaction.transact/4
    (ecto_sql 3.13.3) lib/ecto/adapters/sql.ex:1468: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions