From a6f6942288f674a902de77220d030e8cb4d81f6a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 3 Oct 2024 16:10:07 +0100 Subject: [PATCH] Fixing an issue with corrupt relationship records referencing rows which don't exist, this is a temporary measure as these relationships should be cleaned up correctly but for now ignore any which reference rows which no longer exist. --- .../backend-core/src/db/couch/DatabaseImpl.ts | 12 +++++++---- .../src/db/linkedRows/LinkController.ts | 20 ++++++++++--------- packages/types/src/sdk/db.ts | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/backend-core/src/db/couch/DatabaseImpl.ts b/packages/backend-core/src/db/couch/DatabaseImpl.ts index 274c1b9e93..472e0f44ad 100644 --- a/packages/backend-core/src/db/couch/DatabaseImpl.ts +++ b/packages/backend-core/src/db/couch/DatabaseImpl.ts @@ -213,17 +213,21 @@ export class DatabaseImpl implements Database { async getMultiple( ids: string[], - opts?: { allowMissing?: boolean } + opts?: { allowMissing?: boolean; excludeDocs?: boolean } ): Promise { // get unique ids = [...new Set(ids)] + const includeDocs = !opts?.excludeDocs const response = await this.allDocs({ keys: ids, - include_docs: true, + include_docs: includeDocs, }) const rowUnavailable = (row: RowResponse) => { // row is deleted - key lookup can return this - if (row.doc == null || ("deleted" in row.value && row.value.deleted)) { + if ( + (includeDocs && row.doc == null) || + (row.value && "deleted" in row.value && row.value.deleted) + ) { return true } return row.error === "not_found" @@ -237,7 +241,7 @@ export class DatabaseImpl implements Database { const missingIds = missing.map(row => row.key).join(", ") throw new Error(`Unable to get documents: ${missingIds}`) } - return rows.map(row => row.doc!) + return rows.map(row => (includeDocs ? row.doc! : row.value)) } async remove(idOrDoc: string | Document, rev?: string) { diff --git a/packages/server/src/db/linkedRows/LinkController.ts b/packages/server/src/db/linkedRows/LinkController.ts index 85a160713b..3c0601c9ee 100644 --- a/packages/server/src/db/linkedRows/LinkController.ts +++ b/packages/server/src/db/linkedRows/LinkController.ts @@ -211,19 +211,21 @@ class LinkController { linkedSchema?.type === FieldType.LINK && linkedSchema?.relationshipType === RelationshipType.ONE_TO_MANY ) { - let links = ( - await getLinkDocuments({ - tableId: field.tableId, - rowId: linkId, - }) - ).filter( - link => - link.id !== row._id && link.fieldName === linkedSchema.name + let links = await getLinkDocuments({ + tableId: field.tableId, + rowId: linkId, + fieldName: linkedSchema.name, + }) + + // check all the related rows exist + const foundRecords = await this._db.getMultiple( + links.map(l => l.id), + { allowMissing: true, excludeDocs: true } ) // The 1 side of 1:N is already related to something else // You must remove the existing relationship - if (links.length > 0) { + if (foundRecords.length > 0) { throw new Error( `1:N Relationship Error: Record already linked to another.` ) diff --git a/packages/types/src/sdk/db.ts b/packages/types/src/sdk/db.ts index a081f4f1a2..49baaa5bb1 100644 --- a/packages/types/src/sdk/db.ts +++ b/packages/types/src/sdk/db.ts @@ -133,7 +133,7 @@ export interface Database { exists(docId: string): Promise getMultiple( ids: string[], - opts?: { allowMissing?: boolean } + opts?: { allowMissing?: boolean; excludeDocs?: boolean } ): Promise remove(idOrDoc: Document): Promise remove(idOrDoc: string, rev?: string): Promise