-
Notifications
You must be signed in to change notification settings - Fork 78
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
Implicit conversion from data type varbinary to date is not allowed #124
Comments
@puruzio did you ever figure out a fix? We're running into the same issue |
I did some digging into this and wrote a failing test for it. The issue is caused by Tds.Parameter.fix_data_type/1 which casts a A quick way to almost fix this is to change the function linked above from I should caveat the following by saying that I know almost nothing about the guts of the TDS protocol. That being said, I believe the proper way to fix this is to introduce a I think we need to update Tds.Parameter.fix_data_type to introduce a null parameter type: def fix_data_type(%__MODULE__{type: nil, value: nil} = param) do
%{param | type: :null}
end Then add that Finally the real nitty gritty, Tds.Types.encode_data/3 will need to actually put that null type as bits on the wire, which I don't know how to do. I can hopefully dig into this a little more when I get some time, or otherwise, perhaps someone who knows the intricacies of Ecto guts and the TDS protocol can use what I've sleuthed out and make this change (assuming my suggested change is on the right track). |
@moogle19 @mjaric Hi guys. Wondering if you could weigh in on the above as this is a major pain point for us right now. We'd be happy to create a PR to address this issue but need some guidance about the right way to solve it. Could you weigh in on the above and let us know if we're on the right track here? |
cc: @ewitchin 👆 |
See elixir-ecto#124 for more details. This fix is a complete hack that gets the job done for date columns but fails for the same reason on binary columns. Thankfully for us, we don't have any binary columns, so this works until a correct fix can be made.
@markquezada or @rschenk could you please create simple repo that can I check? Thing is that ecto is layer above tds, and there are a lot of points where this could go wrong. Maybe fix could be done in Ecto.Tds.Adapter instead of in Tds driver. |
Hi @mjaric sorry for the delay, I've made a repo here: https://github.com/rschenk/ecto_tds_null_dates |
@mjaric just making sure you saw the above ☝️ |
Tnx. I will check it |
Hi folks, any news on this issue? I bonked into it today. I was able to work around it by changing the column type to string, but i suspect i'm losing some storage there. any other workarounds that make more sense? |
@mlooney I've not heard any news myself. I made a repo illustrating the problem with a failing test, linked above, but have not heard about any progress towards fixing it |
Any attempt to make If you want to know why is this, here is a bit detailed explanation, and it all relates to INPUT parameters and may affect output parameters also, but rarely. Back then, when ecto was young lib, it was built to support postgresql, then mysql and both backends have ability to tell driver (client lib):
Then TDS came, with all its shame not being honest to your driver. So you had to explicitly specify the type for your input parameter. Prepare statement does not "conclude" of what db type parameter should be, it is dev responsibility.... We tried to make assumptions, yet again there is always edge case that will not work! Simply, SqlServer typing sistem is so strict that some conversions is not possible to do, like you see in PR #162. Now this is when you use parameters, if you don't, then string (true for VARCHAR and not NVARCHAR) usually can be converted to any other type (again almost 🙄, but less edge cases... read it "string encoding issues"). But fear SQL injection, you don't want to do this and ecto will not allow it except, maybe you use framgent macro!!!! Back to the It is probably annoying, but as long as you keep your queries under control/ not scattered around, you should be fine. Make reusable fragments of query. |
One more thing, please 🙏 don't rely o implicit conversion, sometimes it may make your queries ro spil in TEMP leading to slow execution! |
See elixir-ecto#124 for more details. This fix is a complete hack that gets the job done for date columns but fails for the same reason on binary columns. Thankfully for us, we don't have any binary columns, so this works until a correct fix can be made.
I'm pretty new to Elixir and Ecto so I may have misunderstood, but wouldn't having to use |
When working with models (Schema) and using There are two scenarios where issues can arise:
|
Just to clarify, this issue is fundamentally challenging due to the flexibility of the typing system in Elixir. In the TDS protocol, we must allocate storage for a type even if its value is NULL. The size of NULL storage varies depending on the type, making it crucial to specify the correct type in your parameters or allow Storage Definition in TDS
Summary
Due to these variations, it is impossible to directly map Elixir’s |
In hindsight, this issue should probably have been tracked on elixir-ecto/ecto_sql because this comes up when using Ecto with Tds. As mentioned above, if someone uses Tds they are able to set the proper types.
I believe by far the most common issue is people using changesets to update to Here is a minimal end-to-end example with Ecto SQL: # Setup:
# $ docker run -e "ACCEPT_EULA=Y" -e 'MSSQL_SA_PASSWORD=secret1@A' \
# -p 1433:1433 --platform linux/amd64 \
# mcr.microsoft.com/mssql/server:2022-latest
Mix.install([
{:ecto_sql, "~> 3.10"},
{:tds, ">= 0.0.0"}
])
Application.put_env(:myapp, Repo, url: "mssql://sa:secret1!A@localhost/mix_install_examples")
defmodule Repo do
use Ecto.Repo, adapter: Ecto.Adapters.Tds, otp_app: :myapp
end
defmodule Migration do
use Ecto.Migration
def change do
create table("posts") do
add(:date, :date)
end
end
end
defmodule Post do
use Ecto.Schema
schema "posts" do
field(:date, :date)
end
end
defmodule Main do
import Ecto.Query, warn: false
def main do
children = [Repo]
_ = Repo.__adapter__().storage_down(Repo.config())
_ = Repo.__adapter__().storage_up(Repo.config())
{:ok, _} = Supervisor.start_link(children, strategy: :one_for_one)
Ecto.Migrator.run(Repo, [{0, Migration}], :up, all: true)
post = Repo.insert!(%Post{date: ~D[2024-01-01]})
Ecto.Changeset.change(post, date: nil) |> Repo.update!()
# ** (Tds.Error) Line 1 (Error 257): Implicit conversion from data type varbinary to date is not allowed. Use the CONVERT function to run this query.
end
end
Main.main()
100% agreed.
I think so too. I believe we have two options. Option 1. We get known types from changesets/queries and send them to Tds. Here's an early proof of concept:
Option 2. We allow users to set option on changesets/queries with either specifically how nulls should be handled or perhaps more broadly with type hints. @mjaric what do you think about these possible solutions? |
Rig will never work unfortunately, even in if you directly type query into some DB IDE simply because implicit conversation don't work on some-to-any basis, here is the link that documents implicit conversation matrix (I'm attaching matrix also below) None of db types can be implicitly converted to all other types.
I think we only need Option 1, since schemaless changeset requires types to be set too. Question is, if there are code paths that could ignore the type declaration from ecto toward tds driver prepare params function. |
Hmm, this is probably the source of issues, it is first match and ignores type if value is Looks like @rschenk was on right track. I'll make new PR with making sure we at least follow the matrix above in first run, then I'll check MS TDS docs to see how to properly encode with exact token that is specified on schema (or both if it is not risky change) |
Repo.Update fails when the changeset includes a nil date value with an intent to update a date field to set to null. The error message states:
The changeset looks like
The text was updated successfully, but these errors were encountered: