sea-orm icon indicating copy to clipboard operation
sea-orm copied to clipboard

Introducing sqlx-error feature

Open mohs8421 opened this issue 3 years ago • 8 comments

PR Info

Purpose of this feature is to not convert errors given from sqlx into strings to ease further analysis of the error and react to it accordingly. This implementation uses a feature switch and an additional error kind to avoid interfering with existing implementations without this feature enabled.

Adds

  • sqlx-error forwarding

mohs8421 avatar May 19 '22 06:05 mohs8421

@mohs8421 hello! Thank you for the PR, some tests have failed.

ikrivosheev avatar May 28 '22 20:05 ikrivosheev

@mohs8421 hello! Thank you for the PR, some tests have failed.

I saw that, but didn't have time to look deeper into these so far. Besides this change is more a suggestion, and @billy1624 already had a slightly different approach in mind, so it might even be better, if you take over what I did here. That decision is up to you.

mohs8421 avatar Jun 01 '22 05:06 mohs8421

After second thought, I will put this on hold until we come up with a way to generically represent common errors for different db backends. By common errors I mean, foreign key constraint error, unique constraint error...etc.

billy1624 avatar Jun 01 '22 07:06 billy1624

that means going for the "more sophisticated variant", this probably means more enum variants, it might not necessarily be more difficult than this approach. The sqlx error interpreting methods for the db drivers would need to check the error code and tell which variant it is, and perhaps provide a catch all fallback for error codes specific to the database which have not been implemented in sea-orm.

mohs8421 avatar Jun 01 '22 07:06 mohs8421

My main concern is that just throwing out the db depent error isn't ideal. Considering SeaORM can be used on multiple db backend.

billy1624 avatar Jun 01 '22 08:06 billy1624

My main concern is that just throwing out the db depent error isn't ideal. Considering SeaORM can be used on multiple db backend.

If I go at an other approach to this, I will take that thought into account. However, I think it is better to give users of SeaORM the possibility to cope this, instead of making it more difficult to get around that, we could introduce a source enum, which might help interpreting the error in the correct database context for this purpose. But I was thinking this is already available at some point.

The thing with abstraction layers always is that they are leaking pieces of other layers, and the question is how to deal with that. In my opinion it is better to accept this boundary and do this "leaking" intentionally at selective places where it is reasonable.

mohs8421 avatar Jun 02 '22 07:06 mohs8421

As a first thing I fixed the errors, that had occurred.

Now I took a look at the error conversion code again, and the thing is, that if I continue with that better approach, I would not be able to make it as a switchable feature, but as a breaking change, as the errors returned will look differently. Please let me know which way you prefer.

mohs8421 avatar Jun 10 '22 07:06 mohs8421

I don't think adding a new error variant is the way to go for now. I prefer having the context approach (https://docs.rs/anyhow/latest/anyhow/trait.Context.html), so instead of Query(String) we should return a Query(QueryErr) From which, we can hold a dyn Box of the SQLx error object, where the user should be able to retrieve via the ErrContext trait and downcast i.e. https://doc.rust-lang.org/std/any/index.html to the desired concrete error.

Meanwhile, we should think about mapping some common SQL errors to a enum, i.e. column not exist, constraint violation

tyt2y3 avatar Jun 12 '22 12:06 tyt2y3

Example of anyhow context with downcasting: https://github.com/dtolnay/anyhow/blob/master/tests/test_context.rs#L96-L118

billy1624 avatar Aug 12 '22 09:08 billy1624

Example of anyhow context with downcasting: https://github.com/dtolnay/anyhow/blob/master/tests/test_context.rs#L96-L118

I still don't see how this would make it easier to implement the desired behaviour, can you help me out here so I can see what I was missing?

mohs8421 avatar Aug 12 '22 11:08 mohs8421

Okay, forget about anyhow. I don't think we need that on a library. We can adopt thiserror instead haha. Read https://nick.groenen.me/posts/rust-error-handling for more.

Enough talking. So, we'd update DbErr to be something like:

pub enum DbErr {
    /// There was a problem with the database connection
    Conn(ErrContext),
    /// An operation did not execute successfully
    Exec(ErrContext),
    /// An error occurred while performing a query
    Query(ErrContext),
    ...
}

pub struct ErrContext {
    pub message: String,
    pub code: Option<String>,
}

The part where SQLx errors are parsed into DbErr has to be changed as well. We can try to convert sqlx::errors into sqlx::DatabaseError. Then, we can get its error message and code out of it, and place it inside our own ErrContext.

  • https://github.com/SeaQL/sea-orm/blob/d262501a44c048ed36a17bbf661ced3c5193f455/src/driver/sqlx_common.rs#L3-L16

billy1624 avatar Aug 16 '22 10:08 billy1624

Thanks, I guess that article about anyow and thiserror is really helpful on that regard. I will adapt my change to that when I have the opportunity to do this.

mohs8421 avatar Aug 18 '22 20:08 mohs8421

As the tests are passing now, what do you think about the current state? Do you want to pull it as it is, or should I continue by looking at the error codes first for some of the most common errors, or should we do that step afterwards? What remains a little puzzling in my opinion is the fact that postgres treats this as a query error, while the other databases issue an exec error. It should be aligned at some point. (look at the test I wrote)

mohs8421 avatar Aug 24 '22 14:08 mohs8421

Thank you for the contribution. This will be merged to a feature branch and I will work on a few details, in particular I think we should nesting the enum variant.

tyt2y3 avatar Aug 28 '22 03:08 tyt2y3