From 7a9b9e01be68c148d0232f0e367358283074eb95 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Tue, 7 Jul 2026 21:16:48 +0200 Subject: [PATCH] feat: user agnostic database restore (#29512) --- .../src/services/database-backup.service.ts | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/server/src/services/database-backup.service.ts b/server/src/services/database-backup.service.ts index 55d69d37b7..405b9d4d0e 100644 --- a/server/src/services/database-backup.service.ts +++ b/server/src/services/database-backup.service.ts @@ -407,7 +407,7 @@ export class DatabaseBackupService { progressCb?.('restore', progress); }); - await pipeline(sqlStream, progressSource, psql, progressSink); + await pipeline(sqlStream, createSqlOwnerTransformStream(databaseUsername), progressSource, psql, progressSink); try { progressCb?.('migrations', 0.9); @@ -572,3 +572,69 @@ function createSqlProgressStreams(cb: (progress: number) => void) { return [source, sink]; } + +function createSqlOwnerTransformStream(databaseUsername: string) { + const OWNER_MARKER_START = new TextEncoder().encode('OWNER TO '); + const DATA_MARKER_START = new TextEncoder().encode('FROM stdin'); + const LINE_END = new TextEncoder().encode(';'); + + const owner = new TextEncoder().encode(databaseUsername); + + let ownerSequenceIndex = 0; + + let replacingOwnerIndex = 0; + let replacingOwner = false; + + let readingDataIndex = 0; + + let dataPart = false; + + return new PassThrough({ + transform(chunk, _encoding, callback) { + let result = chunk; + if (!dataPart) { + for (let index = 0; index < result.length; index++) { + if (replacingOwner) { + if (result[index] === LINE_END[0]) { + result = Buffer.concat([result.slice(0, index), owner.slice(replacingOwnerIndex), result.slice(index)]); + replacingOwnerIndex = owner.length; + } else { + result[index] = owner[replacingOwnerIndex]; + replacingOwnerIndex++; + } + } + + if (replacingOwnerIndex === owner.length) { + replacingOwner = false; + } + + if (result[index] === OWNER_MARKER_START[ownerSequenceIndex]) { + ownerSequenceIndex++; + } else { + ownerSequenceIndex = 0; + } + + if (ownerSequenceIndex === OWNER_MARKER_START.length) { + ownerSequenceIndex = 0; + replacingOwner = true; + replacingOwnerIndex = 0; + } + + if (result[index] === DATA_MARKER_START[readingDataIndex]) { + readingDataIndex++; + } else { + readingDataIndex = 0; + } + + if (readingDataIndex === DATA_MARKER_START.length) { + dataPart = true; + break; + } + } + } + + this.push(result); + callback(); + }, + }); +}