# SQLAlchemy Column Name Quoting Fix for PostgreSQL **Date:** March 22, 2026 **Issue:** PostgreSQL case sensitivity with `mapped_column` parameter --- ## Problem SQLAlchemy's `name=` parameter generates unquoted column names by default, which PostgreSQL lowercases. When column definitions use uppercase CHECK constraints, they don't match the lowercased column names. **Example:** ```python # Model definition NM: Mapped[Union[int, None]] = mapped_column( Integer, name="NM", # Generates: CREATE TABLE ... ( "NM" INTEGER, ...) nullable=True, ) # SQL generated CREATE TABLE sessions ( "NM" INTEGER, -- PostgreSQL lowercases to "nm" ... CONSTRAINT ck_nm_range CHECK (NM IS NULL OR ...) -- References "NM" (uppercase) ) ``` **Error:** `column "nm" does not exist` (PostgreSQL can't find the uppercase constraint reference) --- ## Solution Use **double-quoted identifiers** to force PostgreSQL to preserve case: ```python # CORRECT - Preserves case NM: Mapped[Union[int, None]] = mapped_column( Integer, name='"NM"', # Generates: CREATE TABLE ... ("NM" INTEGER, ...) nullable=True, ) ``` This generates: ```sql CREATE TABLE sessions ( "NM" INTEGER, -- Preserves "NM" in SQL ... CONSTRAINT ck_nm_range CHECK ("NM" IS NULL OR ...) -- Matches quoted name ) ``` --- ## Applied Fixes ### File: app/models/session.py **Lines 108-119**: Fixed NM and NN column definitions to use `name='"NM"'` and `name='"NN"'` **Before:** ```python NM: Mapped[Union[int, None]] = mapped_column( Integer, name="NM", # ❌ Gets lowercased nullable=True, comment="Nilai Mentah (raw score) [0, 1000]", ) ``` **After:** ```python NM: Mapped[Union[int, None]] = mapped_column( Integer, name='"NM"', # ✅ Preserves case in PostgreSQL nullable=True, comment="Nilai Mentah (raw score) [0, 1000]", ) ``` --- ## Why This Works 1. **Double quotes in Python string** → SQL receives `"NM"` as literal 2. **SQL parser preserves literal inside double quotes** → Column name stays `"NM"` (uppercase) 3. **CHECK constraints match** → Both column and constraint use `"NM"` --- ## Notes - This fix is **PostgreSQL-specific** - Other databases may handle this differently - For **MySQL**: Use backticks: ``NM`` instead of quotes - Best practice: Use lowercase identifiers with underscores: `nm_score`, `nn_score` - This issue affects **all uppercase column names** in CHECK constraints --- ## Testing After applying this fix, restart the application and verify: ```bash cd /www/wwwroot/irt-bank-soal git pull source venv/bin/activate pip install -r requirements.txt pm2 restart irt-bank-soal pm2 logs irt-bank-soal --lines 20 ``` Expected: Application starts successfully without `"column nm does not exist"` error. --- ## Related Documentation - [PostgreSQL Identifier Syntax](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-IDENTIFIERS) - [SQLAlchemy Identifier Quoting](https://docs.sqlalchemy.org/en/20/core/metadata.html#sqlalchemy.schema.sequence.Sequence)