From c4a9a66c60a79856730f576688da6621399dc923 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Sun, 24 May 2026 09:18:59 +0200
Subject: [PATCH] db restore with create db if not exitst
---
modules/system/databaseMigration.py | 84 ++++++++++++++++++++++++++++-
1 file changed, 82 insertions(+), 2 deletions(-)
diff --git a/modules/system/databaseMigration.py b/modules/system/databaseMigration.py
index 2637a61d..6089ad0a 100644
--- a/modules/system/databaseMigration.py
+++ b/modules/system/databaseMigration.py
@@ -545,6 +545,74 @@ def _prepareImport(payload: dict) -> dict:
}
+def _ensureDatabaseExists(dbName: str) -> bool:
+ """Create the PostgreSQL database if it does not yet exist.
+
+ Connects to the ``postgres`` admin database using the same credentials
+ as the target DB. Returns True if the database was created, False if
+ it already existed.
+ """
+ registeredDbs = getRegisteredDatabases()
+ configPrefix = registeredDbs.get(dbName)
+ if configPrefix is None:
+ return False
+
+ hostKey = f"{configPrefix}_HOST" if configPrefix != "DB" else "DB_HOST"
+ portKey = f"{configPrefix}_PORT" if configPrefix != "DB" else "DB_PORT"
+ userKey = f"{configPrefix}_USER" if configPrefix != "DB" else "DB_USER"
+ passwordKey = f"{configPrefix}_PASSWORD_SECRET" if configPrefix != "DB" else "DB_PASSWORD_SECRET"
+
+ adminConn = psycopg2.connect(
+ host=APP_CONFIG.get(hostKey, "localhost"),
+ port=int(APP_CONFIG.get(portKey, 5432)),
+ database="postgres",
+ user=APP_CONFIG.get(userKey),
+ password=APP_CONFIG.get(passwordKey),
+ client_encoding="utf8",
+ )
+ try:
+ adminConn.autocommit = True
+ with adminConn.cursor() as cur:
+ cur.execute("SELECT 1 FROM pg_database WHERE datname = %s", (dbName,))
+ if cur.fetchone():
+ return False
+ cur.execute(f'CREATE DATABASE "{dbName}"')
+ logger.info("Created missing database: %s", dbName)
+ return True
+ finally:
+ adminConn.close()
+
+
+def _createTableFromExport(conn, tableName: str, rows: List[dict]) -> None:
+ """Create a table based on the column structure found in the export data.
+
+ Uses TEXT for all columns since we don't have the original DDL.
+ The ``id`` column gets a PRIMARY KEY constraint.
+ """
+ allKeys: List[str] = []
+ seen: set = set()
+ for row in rows:
+ for k in row.keys():
+ if k not in seen:
+ allKeys.append(k)
+ seen.add(k)
+
+ if not allKeys:
+ return
+
+ colDefs = []
+ for col in allKeys:
+ if col == "id":
+ colDefs.append(f'"{col}" TEXT PRIMARY KEY')
+ else:
+ colDefs.append(f'"{col}" TEXT')
+
+ ddl = f'CREATE TABLE IF NOT EXISTS "{tableName}" ({", ".join(colDefs)})'
+ with conn.cursor() as cur:
+ cur.execute(ddl)
+ logger.info("Created table %s with %d columns", tableName, len(allKeys))
+
+
def _importSingleDb(payload: dict, dbName: str, mode: str, protectedIds: List[str]) -> dict:
"""Import a single database from the (already remapped) payload.
@@ -563,11 +631,21 @@ def _importSingleDb(payload: dict, dbName: str, mode: str, protectedIds: List[st
return {"database": dbName, "tables": {}, "recordCount": 0,
"warnings": [f"Keine Daten fuer '{dbName}' im Payload"]}
+ try:
+ dbCreated = _ensureDatabaseExists(dbName)
+ except Exception as e:
+ logger.error("Failed to ensure database %s exists: %s", dbName, e)
+ return {"database": dbName, "tables": {}, "recordCount": 0,
+ "warnings": [f"Datenbank '{dbName}' konnte nicht erstellt werden: {e}"]}
+
protectedIdSet = set(protectedIds)
tables = dbData.get("tables", {})
warnings: List[str] = []
dbResult: Dict[str, int] = {}
+ if dbCreated:
+ warnings.append(f"Datenbank '{dbName}' wurde neu erstellt")
+
conn = _getConnection(dbName)
try:
conn.autocommit = False
@@ -576,9 +654,11 @@ def _importSingleDb(payload: dict, dbName: str, mode: str, protectedIds: List[st
for tableName, rows in tables.items():
if not isinstance(rows, list):
continue
+
if tableName not in existingTables:
- warnings.append(f"Tabelle '{dbName}.{tableName}' existiert nicht, uebersprungen")
- continue
+ _createTableFromExport(conn, tableName, rows)
+ conn.commit()
+ conn.autocommit = False
physicalCols = _getPhysicalColumns(conn, tableName)
if not physicalCols: