Skip to content

ProgrammingError when upserting multiple rows #13107

@zzzeek

Description

@zzzeek

Discussed in #13106

Originally posted by nesati February 3, 2026
I think I found a bug, but the bug reporting page sent me here. Here is an example:

from sqlalchemy import BigInteger, Integer, String, create_engine
from sqlalchemy.dialects.postgresql import insert as postgres_upsert
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"

    user_id: Mapped[int] = mapped_column(BigInteger(), primary_key=True)

    gov_id: Mapped[int] = mapped_column(Integer(), nullable=False, unique=True)

    first_name: Mapped[str] = mapped_column(String(50), nullable=False)
    last_name: Mapped[str] = mapped_column(String(50), nullable=False)


if __name__ == "__main__":
    engine = create_engine("postgresql+psycopg2://localhost")

    with Session(engine) as session:
        Base.metadata.create_all(session.connection())

        stmt = postgres_upsert(User)
        stmt = stmt.on_conflict_do_update(
            index_elements=["gov_id"],
            set_={
                "first_name": stmt.excluded.first_name,
                "last_name": stmt.excluded.last_name,
            },
        )
        list(
            session.scalars(
                stmt.returning(User, sort_by_parameter_order=True),
                [
                    {
                        "gov_id": 1,
                        "first_name": "Jane",
                        "last_name": "Doe",
                    },
                    {
                        "gov_id": 2,
                        "first_name": "John",
                        "last_name": "Smith",
                    },
                ],
            )
        )

This results in

sqlalchemy.exc.ProgrammingError: (psycopg2.errors.InvalidColumnReference) table "imp_sen" has 3 columns available but 4 columns specified

SQL produced by SQLAlchemy seems invalid:

INSERT INTO users (gov_id, first_name, last_name)
SELECT p0::INTEGER, p1::VARCHAR, p2::VARCHAR
FROM (
  VALUES (%(gov_id)s, %(first_name)s, %(last_name)s)
) AS imp_sen(p0, p1, p2, sen_counter)
ORDER BY sen_counter
ON CONFLICT (gov_id)
DO UPDATE SET first_name = excluded.first_name, last_name = excluded.last_name
RETURNING users.user_id, users.gov_id, users.first_name, users.last_name, users.user_id AS user_id__1

Python version: 3.13.2
SQLAlchemy version: 2.0.46
PostgreSQL version: 17.6
psycopg2 version: 2.9.11

This issue seems to occur only when all the conditions are met:

  • there is .on_conflict_do_update
  • there is .returning(sort_by_parameter_order=True)
  • there are multiple rows in the insert

Expected behavior: I'd expect SQLAlchemy to insert or update the given rows and return them in order without raising exception or for this to raise an exception that clearly states that this is not a supported use case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdmlINSERT, UPDATE, DELETE, often with ORMgreat mcveAn issue with a great mcveinsertmanyvaluesinsertmanyvalues featurepostgresql

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions