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
This commit is contained in:
2026-09-25 16:52:28 +02:00
parent 4be948557e
commit 76690c919c
7 changed files with 51 additions and 15 deletions
+3 -3
View File
@@ -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=<random-64-hex>
DATABASE_URL=sqlite:////data/essens.db
DATABASE_URL=postgresql://essens:<passwort>@postgresql.lan:5432/essens?serverVersion=15&charset=utf8
KEYCLOAK_ISSUER=https://iam.ljh31.de/realms/family
KEYCLOAK_CLIENT_ID=essens
KEYCLOAK_CLIENT_SECRET=<keycloak-client-secret>
+1 -1
View File
@@ -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
+40
View File
@@ -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:<pw>@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" <<EOF
BEGIN;
COPY essens_user (id, username, email, kc_sub, created_am, updated_am) FROM STDIN WITH (FORMAT csv);
$(cat "$TMP/essens_user.csv")
\.
COPY essens_mahlzeiten (id, name, notiz, wochentage, sort_order, status, gegessen_am, erstellt_am, aktualisiert_am) FROM STDIN WITH (FORMAT csv);
$(cat "$TMP/essens_mahlzeiten.csv")
\.
SELECT setval('essens_user_id_seq', COALESCE(MAX(id), 0) + 1, false) FROM essens_user;
SELECT setval('essens_mahlzeiten_id_seq', COALESCE(MAX(id), 0) + 1, false) FROM essens_mahlzeiten;
COMMIT;
EOF
echo "== 4) Verifikation"
psql -h "$PG_HOST" -U "$PG_USER" -d "$PG_DB" -c 'SELECT (SELECT count(*) FROM essens_user) AS users, (SELECT count(*) FROM essens_mahlzeiten) AS mahlzeiten;'
-1
View File
@@ -9,7 +9,6 @@ services:
environment:
APP_ENV: prod
APP_DEBUG: '0'
DATABASE_URL: sqlite:////data/essens.db
DEFAULT_URI: https://essen.ljh31.de
KEYCLOAK_ISSUER: https://iam.ljh31.de/realms/family
KEYCLOAK_CLIENT_ID: essen
+1
View File
@@ -7,6 +7,7 @@
"php": ">=8.2",
"ext-ctype": "*",
"ext-iconv": "*",
"ext-pgsql": "*",
"doctrine/doctrine-bundle": "^3.3",
"doctrine/doctrine-migrations-bundle": "^4.0",
"doctrine/orm": "^3.7",
+3 -5
View File
@@ -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');
}
}
+3 -5
View File
@@ -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');
}
}