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

[Pg] Fix: all datetime mappings #1659

Merged
merged 12 commits into from
Jan 9, 2024

Conversation

Angelelz
Copy link
Collaborator

@Angelelz Angelelz commented Dec 16, 2023

This PR will close #806, close #971, close #1176, close #1185, close #1407 and close #1587.
Most are the same. The problem is postgres.js, it didn't have a clear way of replace the default parsing into a Date object after the client have been instantiated.
I spent some time researching and found a solution.
Although it looks like postgres.js is still trimming the microsecond precision, even after bypassing the parsing.

I'll look into it further and possibly submit a new PR.

Added tests on all/most drivers that use postgres.

Edit: I found another workaround for the issue with trimming the microsecond precision. I added the commit.

@Angelelz
Copy link
Collaborator Author

This will also close #1061

@Angelelz Angelelz changed the title Fix all datetime mappings [Pg] Fix: all datetime mappings Dec 17, 2023
@Hebilicious
Copy link

Just hit this issue, thanks for looking into it @Angelelz

Closing 7 issues in one 🚀 🚀 🚀 🚀 🚀 🚀 🚀

@jakeleventhal
Copy link

Am waiting for this to merge before giving a migration from prisma a test

@AndriiSherman AndriiSherman changed the base branch from main to beta December 27, 2023 15:04
@AndriiSherman
Copy link
Member

@Angelelz, thanks for your PR. Are those changes compatible with the previous implementation?
For example, if the user already has timestamp columns with some data and upgrades to this version, will the selected data be retained in the same format?

@Angelelz
Copy link
Collaborator Author

@Angelelz, thanks for your PR. Are those changes compatible with the previous implementation? For example, if the user already has timestamp columns with some data and upgrades to this version, will the selected data be retained in the same format?

Before, the user was being lied to by the types when using the postgres.js driver. Drizzle was expecting to receive a string, and was reporting a string and not doing any mapping, but postgres was actually returning a Date. That's why so many issues were filed. With this PR the mapping made by postgres.js has been disabled, so drizzle maps correctly as the user requests with the mode.
We might need to add a warning on the new version, as the users might have implemented workarounds for this.

BTW, I see conflicts, I thought I resolved them all last night. Let me see if I can resolve here in GH.

@ericodom
Copy link

This is 100% what's happening...even if you set mode = "string" it returns a date object (but the type is a string). I'm having to do a quick transform from date -> string before returning results, just to get the data to sync with the types.

Really looking forward to this PR getting merged :-)

@jakeleventhal
Copy link

Does this resolve #1164

@Angelelz
Copy link
Collaborator Author

Angelelz commented Dec 27, 2023

Does this resolve #1164

Based on this test added, if it passes then yes. But I think that test only runs when merging to beta or main. I didn't run it locally. Crucially, the mapping I did in timestamp.ts should be the fix.

@jakeleventhal
Copy link

Asdf
8 issues resolved now

@jakeleventhal
Copy link

#1059 this one too @Angelelz

@Angelelz
Copy link
Collaborator Author

Does this resolve #1164

Based on this test added, if it passes then yes. But I think that test only runs when merging to beta or main. I didn't run it locally. Crucially, the mapping I did in timestamp.ts should be the fix.

@AndriiSherman Can you run the test on AWS locally to know for sure? I don't have an account, nor I want to create one lol

@Angelelz
Copy link
Collaborator Author

#1059 this one too @Angelelz

Actually, this might have already been fixed by this commit.

@AndriiSherman
Copy link
Member

Does this resolve #1164

Based on this test added, if it passes then yes. But I think that test only runs when merging to beta or main. I didn't run it locally. Crucially, the mapping I did in timestamp.ts should be the fix.

@AndriiSherman Can you run the test on AWS locally to know for sure? I don't have an account, nor I want to create one lol

Yes, I wanted to test Serverless v2 anyway, so I'm going to run all tests for the AWS Data API and merge if successful. Also, @Angelelz, I guess mappings in the timestamp can be reverted. We will always get a string from the driver, as long as in both pg and postgres.js, we are receiving a raw response, which is always a string for dates. I think, in this case, it should be compatible with the previous version and still work as expected.

I also think that it may be a breaking change for someone who is doing custom mapping, so I guess we would need to make a minor release and not a patch release and add this to the docs. Maybe we can finally add release notes or tags to mark it somehow

@jakeleventhal
Copy link

@AndriiSherman related issue: #1686

@AndriiSherman
Copy link
Member

I reverted the mappings to the way it was before, but kept the mapToDriverValue as it was dropping millisecond precision. The problem is that .toUTCString() method drops the millisecond and doesn't really add anything. See #1176

I guess you need to create another tests for timestamp with timezone:

First case(mode: string)

  1. Insert date in string format with timezone in it
  2. Select in string format and check that values are the same
  3. Select as raw query and check that values are the same

Second case(mode: date)

  1. Insert date as new Date()
  2. Select as date and check that timezones are the same
  3. Compare both dates(maybe by using .getTime())

Third case(mode: date) with timestamps inserted from different timezones:

  1. Insert date as new Date() with one timezone
  2. Insert date as new Date() with another timezone
  3. Select both dates and compare both dates(maybe by using .getTime())

And a few more tests for just timestamp without timezone

First case(mode: string)

  1. Insert date in string format without timezone
  2. Select in string format and check that values are the same
  3. Select as raw query and check that values are the same

Second case(mode: string)

  1. Insert date in string format with timezone
  2. Select as raw query and check that values are the same
    Example: You have inserted 2023-12-28T09:29:43.824+03:00, You selected 2023-12-28 09:29:43.824. You need to trim T and timezone from the first date and check that values are the same

Third case(mode: date)

  1. Insert date as new Date()
  2. Select as raw query as string, make this as Date in UTC and compare both dates(maybe by using .getTime())
  3. Compare both dates using orm mapping(maybe by using .getTime())

@Angelelz
Copy link
Collaborator Author

Hey Andrew,
I'll add the test, but when using mode: 'date', remember that the Date object doesn't store the timezone, it just stores the datetime as UTC, and just shows it to you in your local timezone.
There's very little test that can be done around timezones when using the Date object.
I'll add the tests later today and write some comments on the code for your reference.

@Angelelz
Copy link
Collaborator Author

@AndriiSherman I added the tests on all 5 pg drivers. the big problem is that I couldn't run the tests locally on AWS or Neon, because I don't have accounts on either of those. I basically guessed the types of the raw select when using db.execute.
I would suggest you to run them locally if you can, if not, let me know and I'll just erase them.

Copy link
Member

@AndriiSherman AndriiSherman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

integration-tests/tests/pg.test.ts Outdated Show resolved Hide resolved
@Angelelz
Copy link
Collaborator Author

Angelelz commented Jan 3, 2024

@AndriiSherman I have added the requested tests. I also decided to add the requested tests on the postgres.js.test.ts since that's the driver where I made the changes to the mappings.

@AndriiSherman
Copy link
Member

@AndriiSherman I have added the requested tests. I also decided to add the requested tests on the postgres.js.test.ts since that's the driver where I made the changes to the mappings.

just left 1 comment

Copy link
Member

@AndriiSherman AndriiSherman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// 2, Select in string format and check that values are the same
const result = await db.select().from(table);

t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need this specific test, because you have 2 proper tests with different timezones added


// Please notice that postgres will transform the date to UTC and it saves it in UTC.
// even if set without time zone, just becuase it was provided a timezone offset, it will transform it to UTC
t.deepEqual(result.rows, [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Postgres docs states

In a literal that has been determined to be timestamp without time zone, PostgreSQL will silently ignore any time zone indication. That is, the resulting value is derived from the date/time fields in the input value, and is not adjusted for time zone.

This means that when you insert a string with a timezone into a timestamp without time zone type, the timezone should be simply removed without any time conversions. For example, for 2022-01-01T02:00:00.123456-02, you should receive 2022-01-01 02:00:00.123456 in return

The reason why it happened - you have created timestamp(6) with time zone instead of timestamp(6)

await db.execute(sql`
create table ${table} (
id serial primary key,
timestamp_string timestamp(6) with time zone not null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be just timestamp(6)

await db.execute(sql`
create table ${table} (
id serial primary key,
timestamp_string timestamp(3) with time zone not null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be just timestamp(6)

@AndriiSherman
Copy link
Member

@Angelelz, while you are fixing what I found, I'll start writing documentation for timestamps with explanations on how it should work with Postgres now. I want to make sure everybody will know exactly how we are handling it

@Angelelz
Copy link
Collaborator Author

Angelelz commented Jan 4, 2024

@AndriiSherman sounds good, thank you for being so thorough. Will address these later today.

@ggrantrowberry
Copy link

Will this fix the issue where I have date columns in Postgres and Date objects that I am trying to insert but Drizzle makes me convert them to strings first?

@Angelelz
Copy link
Collaborator Author

Angelelz commented Jan 4, 2024

@AndriiSherman You were totally right, I had made the mistake of setting the columns with time zone and thought that postgres were transforming them internally.

@jakeleventhal
Copy link

Nice work @Angelelz and thanks for the review @AndriiSherman

This one is really important for our team

@AndriiSherman
Copy link
Member

This pull request will be merged into the beta and can be tested using drizzle-orm@beta before being merged into the latest. I will provide a detailed explanation of how it should work and will notify in every related issue that this pull request is closing

@AndriiSherman AndriiSherman merged commit 8c0788a into drizzle-team:beta Jan 9, 2024
5 checks passed
@realmikesolo
Copy link
Collaborator

This PR will also close 895

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment