Elixir 1.6.0 (compiled with OTP 20)
elixir:1.6)I have some code that looks like this:
update_fallback =
from(
r in NrRequests,
where: r.guid == ^request.request_id and r.last_seq < ^new_sequence_number,
update: [set: [nr_status_id: ^status_id, last_seq: ^new_sequence_number]]
)
NrRequests.changeset(%NrRequests{}, %{
guid: request.request_id,
nr_status_id: status_id,
last_seq: new_sequence_number
})
|> Repo.insert(on_conflict: update_fallback)
This generates the following error (taken from the ecto debug logs):
13:30:45.851 [debug] QUERY ERROR db=0.1ms
INSERT INTO `nr_requests` (`guid`,`last_seq`,`nr_status_id`,`created_at`,`updated_at`) VALUES (?,?,?,?,?) ON DUPLICATE KEY UPDATE `nr_status_id` = ?, `last_seq` = ? WHERE ((n0.`guid` = ?) AND (n0.`last_seq` < ?)) ["101010", 1, 19, {{2018, 2, 27}, {13, 30, 45, 850780}}, {{2018, 2, 27}, {13, 30, 45, 850789}}, 19, 1, "101010", 1]
Here's a stacktrace:
** (Mariaex.Error) (1064): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE ((n0.`guid` = ?) AND (n0.`last_seq` < ?))' at line 1
code: PublicInterface.Logger.log_status(context.sample_request, "fooStatus", 2)
stacktrace:
(ecto) lib/ecto/adapters/mysql.ex:198: Ecto.Adapters.MySQL.insert/6
(ecto) lib/ecto/repo/schema.ex:547: Ecto.Repo.Schema.apply/4
(ecto) lib/ecto/repo/schema.ex:213: anonymous fn/14 in Ecto.Repo.Schema.do_insert/4
(public_interface) lib/public_interface/logger.ex:27: PublicInterface.Logger.log_status/3
test/public_interface/logger_test.exs:168: (test)
After some googling I've come to realize that mysql doesn't support this kind of construction at all (on duplicate key update ... where). Still, it doesn't feel right that you can generate SQL syntax errors like this.
I could see two possible expected behaviours:
Or possibly the Ecto maintainers are OK with this kind of error coming through. Perhaps Ecto doesn't support every possible combination of inputs (I'd be OK with that outcome as well). It's hard for me to say as a relative newcomer.
I'd be happy to take a look at implementing either of the two possibilities above, but I'd need some guidance as to what would be accepted in Ecto before I invest a lot of time in a solution.
Great catch! I agree that the MySQL adapter should raise, as this construct is available in Postgres but not on MySQL.
To clarify, the MySQL adapter should raise if a query is given as :on_conflict, and the query has a :where.
Ok great! Looking at the code it doesn't seem too difficult, I'll take a crack at it.
Most helpful comment
Ok great! Looking at the code it doesn't seem too difficult, I'll take a crack at it.