fix(backup): match db.schema.table excludes when database equals schema

This commit is contained in:
Melody Ruch
2026-08-27 22:24:09 +08:00
committed by GitHub
parent ec0b6768be
commit 1016ccc352
2 changed files with 22 additions and 2 deletions
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { databaseBackupRunsToPrune, normalizeDatabaseBackupRun, type DatabaseBackupRun } from "../../backup/scheduledDatabaseBackup";
import { databaseBackupRunsToPrune, databaseBackupTableMatchesPattern, normalizeDatabaseBackupRun, resolveScheduledDatabaseBackupTableScope, type DatabaseBackupRun } from "../../backup/scheduledDatabaseBackup";
const startedAt = "2026-08-12T00:00:00.000Z";
@@ -46,3 +46,23 @@ describe("database backup run persistence", () => {
expect(databaseBackupRunsToPrune(normalizedRuns, "schedule-1", 1).map((value) => value.id)).toEqual(["scheduled-old"]);
});
});
describe("database backup table pattern matching", () => {
it("matches a fully-qualified db.schema.table pattern when the database and schema share the same name", () => {
// Regression for #7314: PostgreSQL commonly has a schema with the same
// name as its database, and the fully-qualified candidate was previously
// suppressed whenever database === schema, silently breaking exclude
// rules written as "db.schema.table".
expect(databaseBackupTableMatchesPattern("vector_data", ["issue7314.issue7314.vector_data"], "issue7314", "issue7314")).toBe(true);
});
it("still matches a fully-qualified db.schema.table pattern when the database and schema differ", () => {
expect(databaseBackupTableMatchesPattern("vector_data", ["mydb.myschema.vector_data"], "mydb", "myschema")).toBe(true);
});
it("excludes the matched table from the backup scope when database and schema share the same name", () => {
const scope = resolveScheduledDatabaseBackupTableScope("exclude", ["issue7314.issue7314.vector_data"], ["vector_data", "keep_rows"], "issue7314", "issue7314");
expect(scope.includedTables).toEqual(["keep_rows"]);
expect(scope.excludedTables).toEqual(["vector_data"]);
});
});
@@ -79,7 +79,7 @@ function tablePatternRegex(pattern: string, caseSensitive: boolean): RegExp {
export function databaseBackupTableMatchesPattern(table: string, patterns: readonly string[], database = "", schema = "", caseSensitive = true): boolean {
const candidates = [table];
if (schema) candidates.push(`${schema}.${table}`);
if (database && schema && database !== schema) candidates.push(`${database}.${schema}.${table}`);
if (database && schema) candidates.push(`${database}.${schema}.${table}`);
return patterns.some((pattern) => {
const matcher = tablePatternRegex(pattern, caseSensitive);
return candidates.some((candidate) => matcher.test(candidate));