-
Notifications
You must be signed in to change notification settings - Fork 45
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
datetime_add uses incompatible format #116
Comments
IIRC the |
Hey thanks for the feedback. I'm not familiar with that spec (and just learning Ecto), but considering that SQLite3 doesn't have a native storage class for timestamps, it's on user-land code to represent dates in a consistent way. Is there a different way to write I'll try and look at how it is done for the Postgres' Adapter, but guessing there the problem doesn't exist because of native data types. |
@rhcarvalho you just need to calculate it in the application layer
|
Thanks @warmwaffles, that works. Closing this as probably working as intended! Thanks again! |
I don't think there is a reason we can't support it. https://sqlite.org/lang_datefunc.html |
running into this as well. the |
Okay. I will see what I can figure out. @iwarshak can you share the query you are using? And potentially the schema? |
something like:
my schema was generated from the generators (i.e. using
|
Okay. I'll plug that in later today and dig into the issue more. The built in date functions in sqlite should get us what we need here. |
@iwarshak I can't recreate this. The local schema in the test suite has this CREATE TABLE IF NOT EXISTS "products" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "account_id" INTEGER CONSTRAINT "products_account_id_fkey" REFERENCES "accounts"("id"), "name" TEXT, "description" TEXT, "external_id" TEXT, "bid" TEXT, "tags" TEXT, "approved_at" TEXT, "ordered_at" TEXT, "price" DECIMAL, "inserted_at" TEXT NOT NULL, "updated_at" TEXT NOT NULL); I have a test where I put this in place: test "using built in ecto functions" do
Application.put_env(:ecto_sqlite3, :datetime_type, :text_datetime)
account = insert_account(%{name: "Test"})
insert_product(%{
account_id: account.id,
name: "Foo",
inserted_at: days_ago(1)
})
insert_product(%{
account_id: account.id,
name: "Bar",
inserted_at: days_ago(2)
})
insert_product(%{
account_id: account.id,
name: "Qux",
inserted_at: days_ago(5)
})
assert [
%{name: "Foo"},
%{name: "Bar"}
] =
Product
|> select([p], p)
|> where([p], p.inserted_at >= from_now(-3, "day"))
|> order_by([p], desc: p.inserted_at)
|> TestRepo.all()
end
defp insert_account(attrs) do
%Account{}
|> Account.changeset(attrs)
|> TestRepo.insert!()
end
defp insert_product(attrs) do
%Product{}
|> Product.changeset(attrs)
|> TestRepo.insert!()
end
defp days_ago(days) do
now = DateTime.utc_now()
DateTime.add(now, -days * 24 * 60 * 60, :second)
end The "Qux" product looks like this in the debug
You can check out the work done here. https://github.com/elixir-sqlite/ecto_sqlite3/tree/try-issue-116 If there is a way you can try to recreate the issue reliably in a test, that would be a tremendous help. |
Hey @warmwaffles. Thanks for your great work. I have also stumbled on this, but I come bearing a test! The trick is the Rewriting your test to focus on the time comparison in seconds, rather than days, demonstrates the issue: test "using built in ecto functions" do
account = insert_account(%{name: "Test"})
insert_product(%{
account_id: account.id,
name: "Foo",
inserted_at: seconds_ago(1)
})
insert_product(%{
account_id: account.id,
name: "Bar",
inserted_at: seconds_ago(3)
})
q =
Product
|> select([p], p)
|> where([p], p.inserted_at >= ago(2, "second"))
|> order_by([p], desc: p.inserted_at)
expanded_q =
Ecto.Adapters.SQL.to_sql(:all, TestRepo, q)
|> dbg()
TestRepo.query(elem(expanded_q, 0), elem(expanded_q, 1))
|> dbg()
assert [
%{name: "Foo"},
] =
q
|> TestRepo.all()
end
defp insert_account(attrs) do
%Account{}
|> Account.changeset(attrs)
|> TestRepo.insert!()
end
defp insert_product(attrs) do
%Product{}
|> Product.changeset(attrs)
|> TestRepo.insert!()
end
defp seconds_ago(seconds) do
now = DateTime.utc_now()
DateTime.add(now, -seconds, :second)
end Those
I've whipped up a potential solution I can submit if it looks ~correct (new to Elixir, would love feedback): |
Yea that solution looks super close to being the solution to use. I believe we'd want to use @datetime_type Application.compile_env(:ecto_sqlite3, :datetime_type, :iso8601)
defp expr({:datetime_add, _, [datetime, count, interval]}, sources, query) do
fmt = Ecto.Adapters.SQLite3.Codec.datetime_format(@datetime_type)
If you want to open a PR that'd be fine. Otherwise I can grab the solution you have and play with it some more. Thank you for reproducing the error. |
Fixed under |
Oh cool. Thanks, Matt! Had to step out yesterday, but next time I'll just open the PR 😅 I didn't realize the options were compile time (still wrapping my head around that distinction). The other changes you made make sense. Thanks for getting it merged so quickly! |
Options don't need to be compile time, but I don't think users want to alter the adapter at runtime. The trade off is that every time the the |
Oh duh. That makes sense. Thanks! |
I'm looking at a project (live_beats modified to use SQLite3) with a query like this:
The query was not returning the expected results because the format used to store a datetime in
inserted_at
is different than the one produced byfrom_now
. The former uses eitheriso8601
(%Y-%m-%d %H:%M:%f
, the default) ortext_datetime
(%Y-%m-%d %H:%M:%S
), while the latter produces%Y-%m-%d %H:%M:%f000Z
.It took me some time to understand where those are coming from and I traced it back to:
ecto_sqlite3/lib/ecto/adapters/sqlite3/connection.ex
Lines 1324 to 1334 in eca01c1
ecto_sqlite3/lib/ecto/adapters/sqlite3/codec.ex
Lines 110 to 136 in eca01c1
So I wonder if changing the implementation of
expr({:datetime_add, ...
to match the configured format would be an acceptable change?The text was updated successfully, but these errors were encountered: