Skip to content

PostgresHook: Add configurable UPSERT update fields#67045

Merged
dabla merged 1 commit into
apache:mainfrom
SameerMesiah97:PostgresHook-Upsert-Rows
Jun 16, 2026
Merged

PostgresHook: Add configurable UPSERT update fields#67045
dabla merged 1 commit into
apache:mainfrom
SameerMesiah97:PostgresHook-Upsert-Rows

Conversation

@SameerMesiah97

@SameerMesiah97 SameerMesiah97 commented May 16, 2026

Copy link
Copy Markdown
Contributor

Description

This change extends PostgreSQL UPSERT support by introducing configurable update fields for INSERT ... ON CONFLICT operations.

A new replace_target option is added to PostgresDialect.generate_replace_sql, allowing callers to specify which columns are updated when a conflict occurs. When omitted, the existing behavior of updating all non-conflict columns is preserved. When an empty list is provided, DO NOTHING semantics are generated.

The change also adds a convenience PostgresHook.upsert_rows method that wraps the existing insert_rows(replace=True) implementation and exposes PostgreSQL UPSERT concepts through conflict_fields and update_fields.

Rationale

While PostgreSQL UPSERT support already exists through insert_rows(replace=True), callers cannot currently control which columns are updated when conflicts occur. This change adds that flexibility while preserving existing behavior by default.

The upsert_rows convenience method provides a more intuitive PostgreSQL-specific API without requiring callers to use lower-level dialect arguments.

Tests

Added unit tests verifying that:

  • UPSERT operations correctly support configurable update fields.
  • UPSERT operations correctly support single and composite conflict fields.
  • DO NOTHING behavior is generated when an empty update field list is provided.
  • upsert_rows correctly delegates to the existing insert_rows(replace=True) implementation.

Notes

The entry for the parameter replace in the docstring for generate_replace_sql has been removed as the argument for it is not being used within the function.

Backwards Compatibility

This change is fully backwards compatible.

Existing insert_rows(replace=True) behavior is preserved. Callers that do not specify update fields continue to update all non-conflict columns exactly as before.

@SameerMesiah97 SameerMesiah97 force-pushed the PostgresHook-Upsert-Rows branch from 64de06d to 5709c06 Compare May 16, 2026 20:14
@SameerMesiah97 SameerMesiah97 changed the title Postgres hook upsert rows PostgresHook: Add upsert rows support using ON CONFLICT May 16, 2026

@justinpakzad justinpakzad left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice PR. Left a couple of comments. I know it's still in draft but figured I'd leave some feedback anyways.

Comment thread providers/postgres/src/airflow/providers/postgres/hooks/postgres.py Outdated
Comment thread providers/postgres/src/airflow/providers/postgres/hooks/postgres.py Outdated
Comment thread providers/postgres/src/airflow/providers/postgres/hooks/postgres.py Outdated
Comment thread providers/postgres/src/airflow/providers/postgres/hooks/postgres.py Outdated
Comment thread providers/postgres/src/airflow/providers/postgres/hooks/postgres.py Outdated
@SameerMesiah97

Copy link
Copy Markdown
Contributor Author

Nice PR. Left a couple of comments. I know it's still in draft but figured I'd leave some feedback anyways.

That’s perfectly fine. I will address the feedback once the dependency is merged.

@SameerMesiah97 SameerMesiah97 force-pushed the PostgresHook-Upsert-Rows branch from 5709c06 to a438020 Compare June 5, 2026 21:40
@SameerMesiah97 SameerMesiah97 marked this pull request as ready for review June 5, 2026 22:52
@eladkal eladkal requested review from dabla and shahar1 June 6, 2026 06:37
@dabla

dabla commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Upsert rows is already supported in PostgresHook via insert_rows and insert_args={“replace”: True}

@SameerMesiah97

Copy link
Copy Markdown
Contributor Author

Upsert rows is already supported in PostgresHook via insert_rows and insert_args={“replace”: True}

I will convert this to draft. This either needs to be reworked or closed.

Sorry for overlooking that.

@SameerMesiah97 SameerMesiah97 marked this pull request as draft June 6, 2026 11:00
@dabla

dabla commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Upsert rows is already supported in PostgresHook via insert_rows and insert_args={“replace”: True}

I will convert this to draft. This either needs to be reworked or closed.

Sorry for overlooking that.

You could still implement this method in the PostgresHook as a shortcut convenience method and delegate the call to the insert_rows method with replace=True. And then just test the delegation, I still think this is a good idea.

@SameerMesiah97 SameerMesiah97 force-pushed the PostgresHook-Upsert-Rows branch from a438020 to 2ef09bd Compare June 8, 2026 19:41
@SameerMesiah97 SameerMesiah97 changed the title PostgresHook: Add upsert rows support using ON CONFLICT PostgresHook: Add configurable UPSERT update fields Jun 8, 2026
Extend PostgreSQL ON CONFLICT support by allowing callers to
specify which columns are updated when conflicts occur.

Preserve existing behavior when no update fields are provided,
support DO NOTHING semantics via an empty update field list,
and add an upsert_rows convenience wrapper built on top of the
existing insert_rows(replace=True) implementation.
@SameerMesiah97 SameerMesiah97 force-pushed the PostgresHook-Upsert-Rows branch from 2ef09bd to 9714369 Compare June 8, 2026 20:03
@SameerMesiah97 SameerMesiah97 marked this pull request as ready for review June 9, 2026 21:04
@SameerMesiah97

Copy link
Copy Markdown
Contributor Author

@dabla

I have re-scoped the PR down to a convenience method i.e. upsert_rows that allows the update fields to be configured. This is not currently covered by insert_rows. Naturally, the underlying dialect for postgres had to be ajdusted slightly to accomodate this.

@Dev-iL

Dev-iL commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Naturally, the underlying dialect for postgres had to be ajdusted slightly to accomodate this.

Please elaborate. This is important since we're in the process of switching the default driver for postgres (both sync and async) to psycopg v3.

@SameerMesiah97

Copy link
Copy Markdown
Contributor Author

Naturally, the underlying dialect for postgres had to be ajdusted slightly to accomodate this.

Please elaborate. This is important since we're in the process of switching the default driver for postgres (both sync and async) to psycopg v3.

I have left a comment on the relevant part of the diff.

@dabla

dabla commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Naturally, the underlying dialect for postgres had to be ajdusted slightly to accomodate this.

Please elaborate. This is important since we're in the process of switching the default driver for postgres (both sync and async) to psycopg v3.

This is regarding to the Airflow postgres dialect, not the SQLAlchemy one to be clear, normally this should have zero effect on the used driver.

@dabla

dabla commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@SameerMesiah97 please resolve all comments that have been addressed, this makes review easier.

@dabla dabla merged commit b35ecd5 into apache:main Jun 16, 2026
156 checks passed
@dabla

dabla commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Congratulations @SameerMesiah97 , thank your for the great work!

RulerChen pushed a commit to RulerChen/airflow that referenced this pull request Jun 16, 2026
Extend PostgreSQL ON CONFLICT support by allowing callers to
specify which columns are updated when conflicts occur.

Preserve existing behavior when no update fields are provided,
support DO NOTHING semantics via an empty update field list,
and add an upsert_rows convenience wrapper built on top of the
existing insert_rows(replace=True) implementation.

Co-authored-by: Sameer Mesiah <smesiah971@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants