I have found these related issues/pull requests
Description
Deploying a new enum variant to the database is currently a breaking change for every service instance still running the old binary.
Take this definition:
#[derive(sqlx::Type)]
#[sqlx(type_name = "permission_enum")]
enum Permission {
Add,
Subtract,
}
Suppose we now add a Multiply permission. Since multiplication can be expressed as repeated addition, we grant Multiply to every user who already has Add and Subtract, and add the value to the database enum. The moment those rows exist, every instance still running the previous version fails when loading user permissions, because the derived Decode impl ends in:
invalid value "Multiply" for enum Permission
This makes rolling deployments impossible without a coordinated upgrade.
Prefered solution
Add a variant-level #[sqlx(other)] attribute, similar to serde's, marking the variant that unrecognized database values decode into. The variant captures the original value so it isn't lost:
#[derive(sqlx::Type)]
#[sqlx(type_name = "permission_enum")]
enum Permission {
Add,
Subtract,
#[sqlx(other)]
Unknown(String),
}
In the derived Decode impl would then replace the error arm with the unknown variant. For #[repr(..)] enums the payload would be the repr type (e.g. Unknown(i32)).
Open questions
#[derive(sqlx::Type)] also generates Encode, and encoding Unknown(s) back as s round-trips correctly and seems like the least surprising behavior, but it does let a hand-constructed Unknown("nonsense") reach the database. The alternative is to return an encode error when encountering Unknown.
Should a bare unit #[sqlx(other)] Unknown be allowed? It's convenient, but it has no meaningful encoding and discards information, so requiring the payload may be better.
Is this a breaking change? Why or why not?
No, the new attribute is optional, and existing Unknown variants will be untouched.
I'm happy to implement this unless someone beats me to it. I'm opening the issue first to gather feedback on the open questions above.
I have found these related issues/pull requests
Description
Deploying a new enum variant to the database is currently a breaking change for every service instance still running the old binary.
Take this definition:
Suppose we now add a
Multiplypermission. Since multiplication can be expressed as repeated addition, we grantMultiplyto every user who already hasAddandSubtract, and add the value to the database enum. The moment those rows exist, every instance still running the previous version fails when loading user permissions, because the derivedDecodeimpl ends in:This makes rolling deployments impossible without a coordinated upgrade.
Prefered solution
Add a variant-level
#[sqlx(other)]attribute, similar to serde's, marking the variant that unrecognized database values decode into. The variant captures the original value so it isn't lost:In the derived
Decodeimpl would then replace the error arm with the unknown variant. For#[repr(..)]enums the payload would be the repr type (e.g.Unknown(i32)).Open questions
#[derive(sqlx::Type)]also generatesEncode, and encodingUnknown(s)back assround-trips correctly and seems like the least surprising behavior, but it does let a hand-constructedUnknown("nonsense")reach the database. The alternative is to return an encode error when encounteringUnknown.Should a bare unit
#[sqlx(other)] Unknownbe allowed? It's convenient, but it has no meaningful encoding and discards information, so requiring the payload may be better.Is this a breaking change? Why or why not?
No, the new attribute is optional, and existing
Unknownvariants will be untouched.I'm happy to implement this unless someone beats me to it. I'm opening the issue first to gather feedback on the open questions above.