From 76690c919c6bfd6fd00d41bcf475f63c4c10ad3d Mon Sep 17 00:00:00 2001 From: Jens Date: Fri, 25 Sep 2026 16:52:28 +0200 Subject: [PATCH] Migriere Persistenz von SQLite zu PostgreSQL (postgresql.lan:5432/essens) - Dockerfile: pdo_pgsql installieren - migrations: Postgres-Dialekt (SERIAL, TIMESTAMP(0), TEXT) - compose/.env(.dist): postgresql://DATABASE_URL (Passwort via .env.local) - bin/migrate-sqlite-to-pg.sh: einmaliger Datenimport SQLite->PG --- .env.dist | 6 ++--- Dockerfile | 2 +- bin/migrate-sqlite-to-pg.sh | 40 ++++++++++++++++++++++++++++ compose.yaml | 1 - composer.json | 1 + migrations/Version20260913204759.php | 8 +++--- migrations/Version20260918213517.php | 8 +++--- 7 files changed, 51 insertions(+), 15 deletions(-) create mode 100755 bin/migrate-sqlite-to-pg.sh diff --git a/.env.dist b/.env.dist index 6f9060c..0ff8e2d 100644 --- a/.env.dist +++ b/.env.dist @@ -1,10 +1,10 @@ -# Referenzwerte für die Produktion (docker1: /root/stacks/svc-essensplanung/.env, +# Referenzwerte für die Produktion (docker1: /root/stacks/svc-essensplanung/.env.local, # als env_file über compose gemountet – siehe Doku/plan.md M5). -# Lokale Entwicklung nutzt stattdessen .env (var/data.db). +# Lokale Entwicklung nutzt die gleiche DB (postgresql.lan). APP_ENV=prod APP_DEBUG=0 APP_SECRET= -DATABASE_URL=sqlite:////data/essens.db +DATABASE_URL=postgresql://essens:@postgresql.lan:5432/essens?serverVersion=15&charset=utf8 KEYCLOAK_ISSUER=https://iam.ljh31.de/realms/family KEYCLOAK_CLIENT_ID=essens KEYCLOAK_CLIENT_SECRET= diff --git a/Dockerfile b/Dockerfile index 8763a21..258cfa2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends git unzip sqlite3 libicu-dev \ && rm -rf /var/lib/apt/lists/* -RUN docker-php-ext-install bcmath intl +RUN docker-php-ext-install bcmath intl pdo_pgsql COPY --from=composer:2 /usr/bin/composer /usr/bin/composer diff --git a/bin/migrate-sqlite-to-pg.sh b/bin/migrate-sqlite-to-pg.sh new file mode 100755 index 0000000..f3675ff --- /dev/null +++ b/bin/migrate-sqlite-to-pg.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Einmalige Migration: SQLite (/data/essens.db, Volume essens_data) -> PostgreSQL (postgresql.lan/essens) +# Läuft auf docker1 im Stack-Verzeichnis. Erwartet: .env.local enthält DATABASE_URL="postgresql://essens:@postgresql.lan:5432/essens?..." +set -euo pipefail +cd "$(dirname "$0")/.." + +DB_URL=$(grep -m1 '^DATABASE_URL=' .env.local | sed 's/^DATABASE_URL=//; s/^"//; s/"$//') +PG_USER=$(sed -n 's|.*://\([^:@]*\):.*|\1|p' <<<"$DB_URL") +PG_PASS=$(sed -n 's|.*://[^:@]*:\([^@]*\)@.*|\1|p' <<<"$DB_URL") +PG_HOST=$(sed -n 's|.*@\([^:/?]*\).*|\1|p' <<<"$DB_URL") +PG_DB=$(sed -n 's|.*:/\([^?]*\).*|\1|p' <<<"$DB_URL") +export PGPASSWORD="$PG_PASS" + +echo "== 1) SQLite-Export (CSV) aus dem laufenden Container" +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT +docker exec svc-essensplanung sh -c 'sqlite3 -cmd ".nullvalue NULL" -csv /data/essens.db "SELECT * FROM essens_user;"' > "$TMP/essens_user.csv" +docker exec svc-essensplanung sh -c 'sqlite3 -cmd ".nullvalue NULL" -csv /data/essens.db "SELECT * FROM essens_mahlzeiten;"' > "$TMP/essens_mahlzeiten.csv" +echo " essens_user: $(wc -l < "$TMP/essens_user.csv") Zeilen" +echo " essens_mahlzeiten: $(wc -l < "$TMP/essens_mahlzeiten.csv") Zeilen" + +echo "== 2) Schema-Migrations auf PostgreSQL" +docker compose run --rm --no-deps app bin/console doctrine:migrations:migrate --no-interaction + +echo "== 3) Datenimport via COPY" +psql -h "$PG_HOST" -U "$PG_USER" -d "$PG_DB" <=8.2", "ext-ctype": "*", "ext-iconv": "*", + "ext-pgsql": "*", "doctrine/doctrine-bundle": "^3.3", "doctrine/doctrine-migrations-bundle": "^4.0", "doctrine/orm": "^3.7", diff --git a/migrations/Version20260913204759.php b/migrations/Version20260913204759.php index bdc5182..a02fe45 100644 --- a/migrations/Version20260913204759.php +++ b/migrations/Version20260913204759.php @@ -8,26 +8,24 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** - * Auto-generated Migration: Please modify to your needs! + * Postgres-Port der ursprünglichen SQLite-Migration (2026-09-13). */ final class Version20260913204759 extends AbstractMigration { public function getDescription(): string { - return ''; + return 'essens_user (Postgres)'; } public function up(Schema $schema): void { - // this up() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TABLE essens_user (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, kc_sub VARCHAR(64) NOT NULL, created_am DATETIME NOT NULL, updated_am DATETIME NOT NULL)'); + $this->addSql('CREATE TABLE essens_user (id SERIAL PRIMARY KEY, username VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, kc_sub VARCHAR(64) NOT NULL, created_am TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, updated_am TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL)'); $this->addSql('CREATE UNIQUE INDEX uniq_email ON essens_user (email)'); $this->addSql('CREATE UNIQUE INDEX uniq_kc_sub ON essens_user (kc_sub)'); } public function down(Schema $schema): void { - // this down() migration is auto-generated, please modify it to your needs $this->addSql('DROP TABLE essens_user'); } } diff --git a/migrations/Version20260918213517.php b/migrations/Version20260918213517.php index e273375..a260639 100644 --- a/migrations/Version20260918213517.php +++ b/migrations/Version20260918213517.php @@ -8,26 +8,24 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** - * Auto-generated Migration: Please modify to your needs! + * Postgres-Port der ursprünglichen SQLite-Migration (2026-09-18). */ final class Version20260918213517 extends AbstractMigration { public function getDescription(): string { - return ''; + return 'essens_mahlzeiten (Postgres)'; } public function up(Schema $schema): void { - // this up() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TABLE essens_mahlzeiten (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, notiz CLOB DEFAULT NULL, wochentage VARCHAR(32) DEFAULT NULL, sort_order INTEGER NOT NULL, status VARCHAR(32) NOT NULL, gegessen_am DATETIME DEFAULT NULL, erstellt_am DATETIME NOT NULL, aktualisiert_am DATETIME NOT NULL)'); + $this->addSql('CREATE TABLE essens_mahlzeiten (id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, notiz TEXT DEFAULT NULL, wochentage VARCHAR(32) DEFAULT NULL, sort_order INTEGER NOT NULL, status VARCHAR(32) NOT NULL, gegessen_am TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, erstellt_am TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, aktualisiert_am TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL)'); $this->addSql('CREATE INDEX idx_status ON essens_mahlzeiten (status)'); $this->addSql('CREATE INDEX idx_sort_order ON essens_mahlzeiten (sort_order)'); } public function down(Schema $schema): void { - // this down() migration is auto-generated, please modify it to your needs $this->addSql('DROP TABLE essens_mahlzeiten'); } }