feat: migrate database driver from MariaDB to PostgreSQL

Switch from pymysql to psycopg2-binary, update connection string to
postgresql+psycopg2, replace MySQL-specific JSON imports with generic
SQLAlchemy JSON, convert MySQL prefix index to PostgreSQL left() function,
and remove FetchedValue() (unnecessary with PostgreSQL).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
CCS Admin 2026-03-02 08:25:05 +00:00
parent bbcb0c489a
commit e9eb98119f
8 changed files with 11 additions and 15 deletions

View file

@ -1,6 +1,6 @@
# Database
DB_HOST=
DB_PORT=3306
DB_PORT=5432
DB_NAME=dak_c2s
DB_USER=dak_c2s_admin
DB_PASSWORD=

View file

@ -8,7 +8,7 @@ from functools import lru_cache
class Settings(BaseSettings):
# Database
DB_HOST: str = "localhost"
DB_PORT: int = 3306
DB_PORT: int = 5432
DB_NAME: str = "dak_c2s"
DB_USER: str = "dak_c2s_admin"
DB_PASSWORD: str = ""
@ -37,8 +37,8 @@ class Settings(BaseSettings):
def database_url(self) -> str:
password = quote_plus(self.DB_PASSWORD)
return (
f"mysql+pymysql://{self.DB_USER}:{password}"
f"@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}?charset=utf8mb4"
f"postgresql+psycopg2://{self.DB_USER}:{password}"
f"@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
)
class Config:

View file

@ -12,11 +12,11 @@ from sqlalchemy import (
ForeignKey,
Index,
Integer,
JSON,
String,
Text,
func,
)
from sqlalchemy.dialects.mysql import JSON
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base

View file

@ -21,7 +21,6 @@ from sqlalchemy import (
text,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.schema import FetchedValue
from app.database import Base
@ -115,7 +114,6 @@ class Case(Base):
nullable=False,
server_default=func.now(),
onupdate=func.now(),
server_onupdate=FetchedValue(),
)
updated_by: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
@ -154,9 +152,9 @@ class Case(Base):
Index("idx_fallgruppe", "fallgruppe"),
Index("idx_datum", "datum"),
Index("idx_nachname_vorname", "nachname", "vorname"),
# Note: idx_pending_icd uses a prefix length icd(20) which SQLAlchemy
# does not support natively. We use text() for the prefix column.
Index("idx_pending_icd", "jahr", "kw", "fallgruppe", text("icd(20)")),
# Note: idx_pending_icd uses a functional index on left(icd, 20).
# We use text() for the expression column.
Index("idx_pending_icd", "jahr", "kw", "fallgruppe", text("left(icd, 20)")),
Index("idx_pending_coding", "gutachten", "gutachten_typ"),
CheckConstraint(
"fallgruppe IN ('onko','kardio','intensiv','galle','sd')",

View file

@ -11,12 +11,12 @@ from sqlalchemy import (
ForeignKey,
Index,
Integer,
JSON,
SmallInteger,
String,
UniqueConstraint,
func,
)
from sqlalchemy.dialects.mysql import JSON
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base

View file

@ -17,7 +17,6 @@ from sqlalchemy import (
func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.schema import FetchedValue
from app.database import Base
@ -59,7 +58,6 @@ class User(Base):
nullable=False,
server_default=func.now(),
onupdate=func.now(),
server_onupdate=FetchedValue(),
)
# Relationships

View file

@ -3,7 +3,7 @@ uvicorn[standard]==0.34.0
gunicorn==23.0.0
sqlalchemy==2.0.36
alembic==1.14.1
pymysql==1.1.1
psycopg2-binary==2.9.10
cryptography==44.0.0
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4

View file

@ -1,6 +1,6 @@
[Unit]
Description=DAK Zweitmeinungs-Portal Backend (FastAPI/Uvicorn)
After=network.target mariadb.service
After=network.target postgresql.service
[Service]
Type=simple