Fixed foreign-key (mariadb) errors.

When using MariaDB v10.5+ Foreign-Key errors were popping up because of
some changes in that version. To mitigate this on MariaDB and other
MySQL forks those errors are now catched, and instead of a replace_into
an update will happen. I have tested this as thorough as possible with
MariaDB 10.5, 10.4, 10.3 and the default MySQL on Ubuntu Focal. And
tested it again using sqlite, all seems to be ok on all tables.

resolves #1081. resolves #1065, resolves #1050
This commit is contained in:
BlackDex 2020-09-22 12:13:02 +02:00
parent 2f3e18caa9
commit 978be0b4a9
8 changed files with 170 additions and 45 deletions

View File

@ -63,10 +63,21 @@ impl Attachment {
pub fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn:
sqlite, mysql {
diesel::replace_into(attachments::table)
match diesel::replace_into(attachments::table)
.values(AttachmentDb::to_db(self))
.execute(conn)
.map_res("Error saving attachment")
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(attachments::table)
.filter(attachments::id.eq(&self.id))
.set(AttachmentDb::to_db(self))
.execute(conn)
.map_res("Error saving attachment")
}
Err(e) => Err(e.into()),
}.map_res("Error saving attachment")
}
postgresql {
let value = AttachmentDb::to_db(self);

View File

@ -197,11 +197,22 @@ impl Cipher {
db_run! { conn:
sqlite, mysql {
diesel::replace_into(ciphers::table)
match diesel::replace_into(ciphers::table)
.values(CipherDb::to_db(self))
.execute(conn)
.map_res("Error saving cipher")
}
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(ciphers::table)
.filter(ciphers::uuid.eq(&self.uuid))
.set(CipherDb::to_db(self))
.execute(conn)
.map_res("Error saving cipher")
}
Err(e) => Err(e.into()),
}.map_res("Error saving cipher")
}
postgresql {
let value = CipherDb::to_db(self);
diesel::insert_into(ciphers::table)

View File

@ -69,10 +69,21 @@ impl Collection {
db_run! { conn:
sqlite, mysql {
diesel::replace_into(collections::table)
match diesel::replace_into(collections::table)
.values(CollectionDb::to_db(self))
.execute(conn)
.map_res("Error saving collection")
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(collections::table)
.filter(collections::uuid.eq(&self.uuid))
.set(CollectionDb::to_db(self))
.execute(conn)
.map_res("Error saving collection")
}
Err(e) => Err(e.into()),
}.map_res("Error saving collection")
}
postgresql {
let value = CollectionDb::to_db(self);
@ -247,7 +258,7 @@ impl CollectionUser {
db_run! { conn:
sqlite, mysql {
diesel::replace_into(users_collections::table)
match diesel::replace_into(users_collections::table)
.values((
users_collections::user_uuid.eq(user_uuid),
users_collections::collection_uuid.eq(collection_uuid),
@ -255,7 +266,24 @@ impl CollectionUser {
users_collections::hide_passwords.eq(hide_passwords),
))
.execute(conn)
.map_res("Error adding user to collection")
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(users_collections::table)
.filter(users_collections::user_uuid.eq(user_uuid))
.filter(users_collections::collection_uuid.eq(collection_uuid))
.set((
users_collections::user_uuid.eq(user_uuid),
users_collections::collection_uuid.eq(collection_uuid),
users_collections::read_only.eq(read_only),
users_collections::hide_passwords.eq(hide_passwords),
))
.execute(conn)
.map_res("Error adding user to collection")
}
Err(e) => Err(e.into()),
}.map_res("Error adding user to collection")
}
postgresql {
diesel::insert_into(users_collections::table)
@ -346,6 +374,9 @@ impl CollectionCipher {
db_run! { conn:
sqlite, mysql {
// Not checking for ForeignKey Constraints here.
// Table ciphers_collections does not have ForeignKey Constraints which would cause conflicts.
// This table has no constraints pointing to itself, but only to others.
diesel::replace_into(ciphers_collections::table)
.values((
ciphers_collections::cipher_uuid.eq(cipher_uuid),

View File

@ -76,10 +76,21 @@ impl Folder {
db_run! { conn:
sqlite, mysql {
diesel::replace_into(folders::table)
match diesel::replace_into(folders::table)
.values(FolderDb::to_db(self))
.execute(conn)
.map_res("Error saving folder")
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(folders::table)
.filter(folders::uuid.eq(&self.uuid))
.set(FolderDb::to_db(self))
.execute(conn)
.map_res("Error saving folder")
}
Err(e) => Err(e.into()),
}.map_res("Error saving folder")
}
postgresql {
let value = FolderDb::to_db(self);
@ -138,6 +149,9 @@ impl FolderCipher {
pub fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn:
sqlite, mysql {
// Not checking for ForeignKey Constraints here.
// Table folders_ciphers does not have ForeignKey Constraints which would cause conflicts.
// This table has no constraints pointing to itself, but only to others.
diesel::replace_into(folders_ciphers::table)
.values(FolderCipherDb::to_db(self))
.execute(conn)

View File

@ -58,10 +58,21 @@ impl OrgPolicy {
pub fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn:
sqlite, mysql {
diesel::replace_into(org_policies::table)
match diesel::replace_into(org_policies::table)
.values(OrgPolicyDb::to_db(self))
.execute(conn)
.map_res("Error saving org_policy")
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(org_policies::table)
.filter(org_policies::uuid.eq(&self.uuid))
.set(OrgPolicyDb::to_db(self))
.execute(conn)
.map_res("Error saving org_policy")
}
Err(e) => Err(e.into()),
}.map_res("Error saving org_policy")
}
postgresql {
let value = OrgPolicyDb::to_db(self);

View File

@ -206,10 +206,22 @@ impl Organization {
db_run! { conn:
sqlite, mysql {
diesel::replace_into(organizations::table)
match diesel::replace_into(organizations::table)
.values(OrganizationDb::to_db(self))
.execute(conn)
.map_res("Error saving organization")
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(organizations::table)
.filter(organizations::uuid.eq(&self.uuid))
.set(OrganizationDb::to_db(self))
.execute(conn)
.map_res("Error saving organization")
}
Err(e) => Err(e.into()),
}.map_res("Error saving organization")
}
postgresql {
let value = OrganizationDb::to_db(self);
@ -345,10 +357,21 @@ impl UserOrganization {
db_run! { conn:
sqlite, mysql {
diesel::replace_into(users_organizations::table)
match diesel::replace_into(users_organizations::table)
.values(UserOrganizationDb::to_db(self))
.execute(conn)
.map_res("Error adding user to organization")
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(users_organizations::table)
.filter(users_organizations::uuid.eq(&self.uuid))
.set(UserOrganizationDb::to_db(self))
.execute(conn)
.map_res("Error adding user to organization")
}
Err(e) => Err(e.into()),
}.map_res("Error adding user to organization")
}
postgresql {
let value = UserOrganizationDb::to_db(self);

View File

@ -73,10 +73,21 @@ impl TwoFactor {
pub fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn:
sqlite, mysql {
diesel::replace_into(twofactor::table)
match diesel::replace_into(twofactor::table)
.values(TwoFactorDb::to_db(self))
.execute(conn)
.map_res("Error saving twofactor")
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(twofactor::table)
.filter(twofactor::uuid.eq(&self.uuid))
.set(TwoFactorDb::to_db(self))
.execute(conn)
.map_res("Error saving twofactor")
}
Err(e) => Err(e.into()),
}.map_res("Error saving twofactor")
}
postgresql {
let value = TwoFactorDb::to_db(self);

View File

@ -175,10 +175,21 @@ impl User {
db_run! {conn:
sqlite, mysql {
diesel::replace_into(users::table) // Insert or update
.values(&UserDb::to_db(self))
match diesel::replace_into(users::table)
.values(UserDb::to_db(self))
.execute(conn)
.map_res("Error saving user")
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(users::table)
.filter(users::uuid.eq(&self.uuid))
.set(UserDb::to_db(self))
.execute(conn)
.map_res("Error saving user")
}
Err(e) => Err(e.into()),
}.map_res("Error saving user")
}
postgresql {
let value = UserDb::to_db(self);
@ -290,6 +301,8 @@ impl Invitation {
db_run! {conn:
sqlite, mysql {
// Not checking for ForeignKey Constraints here
// Table invitations does not have any ForeignKey Constraints.
diesel::replace_into(invitations::table)
.values(InvitationDb::to_db(self))
.execute(conn)