Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support select: map(x, fields), select_merge: %{...} in source query for insert_all #4430

Closed
nmk opened this issue Jun 13, 2024 · 6 comments · Fixed by #4460
Closed

Support select: map(x, fields), select_merge: %{...} in source query for insert_all #4430

nmk opened this issue Jun 13, 2024 · 6 comments · Fixed by #4460
Labels

Comments

@nmk
Copy link

nmk commented Jun 13, 2024

Elixir version

1.16.3

Database and Version

PostgreSQL 15.0

Ecto Versions

master

Database Adapter and Versions (postgrex, myxql, etc)

postgrex

Current behavior

Repo.insert_all(Foo, from x in Foo, select: %{foo: x.foo, bar: x.bar}, select_merge: %{baz: some_fun(x.baz)})

works as intended.

Repo.insert_all(Foo, from x in Foo, select: map(x, [:foo, :bar]), select_merge: %{baz: some_fun(x.baz)})

raises an ArgumentError stating that the source query does not select into a map.

Expected behavior

The above two snippets should both be supported as they should result in the same source query being produced.

I would be happy to take a stab at this if this is indeed the intended behaviour.

@nmk nmk added the Kind:Bug label Jun 13, 2024
@josevalim
Copy link
Member

Please take a stab at it. It may be that making it work is just too complex, and then we could improve the error message, but we should definitely try it!

@nmk
Copy link
Author

nmk commented Jun 13, 2024

Please take a stab at it. It may be that making it work is just too complex, and then we could improve the error message, but we should definitely try it!

I have made a PR which adds a test and the simplest code to make the test pass.

#4431

As far as I can see the test (as others in the same vein) only assert that a query would be successfully created, not that it would be correct. Is there a way to assert on the query itself?

@aglassman
Copy link

aglassman commented Jun 13, 2024

Using insert_all also fails with struct/2, and %{c | field: ^val}. These are all cases that work with select merge normally, but fail for insert_all. Just happened to dig into this today.

  # This does not work!
  def test_1() do
    # initial query
    query = from(c in Click, select: c, limit: 1)

    Ecto.Multi.new()
    |> Ecto.Multi.insert_all(:click, Click, query)
    |> Repo.transaction()
  end

  # This does not work!
  def test_2() do
    # initial query
    query = from(c in Click, select: struct(c, [:short_code]), limit: 1)

    Ecto.Multi.new()
    |> Ecto.Multi.insert_all(:click, Click, query)
    |> Repo.transaction()
  end

  # This does not work!
  def test_3(short_code_override \\ "default sc") do
    # initial query
    query = from(c in Click, select: %{c | short_code: ^short_code_override}, limit: 1)

    Ecto.Multi.new()
    |> Ecto.Multi.insert_all(:click, Click, query)
    |> Repo.transaction()
  end

@greg-rychlewski
Copy link
Member

Hi @aglassman ,

Something like this will not work because the update syntax is essentially post processing. Whatever is returned from the query will later be updated in Elixir. Thus it can't work for insert_all where you need everything to happen in the database.

 query = from(c in Click, select: %{c | short_code: ^short_code_override}, limit: 1)

But this should work. I tested it:

query = from(c in Click, select: struct(c, [:short_code]), limit: 1)

Maybe your ecto version is old? If you try on the latest commit does it still happen?

@greg-rychlewski
Copy link
Member

I thought maybe it would make sense to select the entire struct. But after playing around with it there are quite a few issues. Mostly because you will be selecting primary keys and foreign keys which will cause issues in a lot of cases.

@aglassman
Copy link

aglassman commented Jun 18, 2024

@greg-rychlewski Thanks for the explanation into why the update syntax does not work, that is good to know. As for struct/2 usage, I mistakingly assumed the error was related to the insert_all select using struct, when it was actually a constraint violation (inserted_at is required). My apologies on that one!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants