Repository navigation
Postgres: support WHERE index_predicate in the ON CONFLICT target - #2627
zvonimir-dd wants to merge 4 commits into
Conversation
In PostgreSQL a column-list conflict target may end with `WHERE index_predicate`, which lets a partial unique index be inferred as the arbiter: `ON CONFLICT (a) WHERE b > 0 DO NOTHING`. Parse it into a new `OnConflict::conflict_target_predicate` and print it back after the target. It is only accepted after a column list, as in PostgreSQL's grammar, and it is separate from the action's `DO UPDATE ... WHERE`. Fixes apache#2625. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2627 +/- ##
==========================================
+ Coverage 81.16% 81.25% +0.09%
==========================================
Files 42 42
Lines 33769 33902 +133
Branches 33769 33902 +133
==========================================
+ Hits 27408 27547 +139
+ Misses 2797 2780 -17
- Partials 3564 3575 +11 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Covers `OnConflict`'s span with and without the predicate, a predicate cut off at the end of the input, and a predicate with no action after it. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LucaCappelletti94
left a comment
There was a problem hiding this comment.
Thanks for the PR. The parsing and the tests look right. I'd go with the struct variant you offered in #2625, ConflictTarget::Columns { columns: Vec<Ident>, predicate: Option<Expr> }, instead of the separate OnConflict::conflict_target_predicate field.
Also do add an example of this syntax for the fuzzer corpus.
As suggested in review: `ConflictTarget::Columns` is now a struct variant, `Columns { columns,
predicate }`, instead of `OnConflict` carrying a separate `conflict_target_predicate`. The
predicate belongs to the column-list target only, and the variant now says so; it also leaves
room for the `( index_expression )` form of apache#1977.
Adds the syntax to the fuzz seed corpus.
Environment: Datadog workspace
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks! Done in ff41dab: |
LucaCappelletti94
left a comment
There was a problem hiding this comment.
Other than this nit, the code looks okay. If you have time, I would appreciate if you could also do some reviews of the other PRs so as to speed up the whole queue.
| /// | ||
| /// See <https://www.postgresql.org/docs/current/sql-insert.html> |
There was a problem hiding this comment.
You should say SQLite accepts this predicate too. Its upsert grammar has the same ( indexed-column, ... ) WHERE expr target, and the parser reads it in every dialect. SQLite 3.51.1 runs INSERT INTO t VALUES (1, 2, 3) ON CONFLICT (a) WHERE b > 0 DO NOTHING and rejects ON CONFLICT WHERE b > 0. With the doc naming only PostgreSQL, a later change could gate the parse to PostgreSQL and drop SQLite without anyone noticing.
| /// | |
| /// See <https://www.postgresql.org/docs/current/sql-insert.html> | |
| /// Target specified as a list of columns, optionally followed by | |
| /// `WHERE index_predicate`, which lets a partial unique index be inferred as the arbiter: | |
| /// `ON CONFLICT (a) WHERE b > 0 DO ...`. PostgreSQL and SQLite both accept the predicate. | |
| /// | |
| /// See <https://www.postgresql.org/docs/current/sql-insert.html> | |
| /// and <https://www.sqlite.org/lang_upsert.html> |
There was a problem hiding this comment.
Thanks, applied your suggestion as written in 8936117. I also added parse_on_conflict_target_predicate to tests/sqlparser_sqlite.rs, so restricting the parse to PostgreSQL would fail a test rather than only contradict the doc.
I checked it against SQLite 3.37.2 as well. With a partial unique index on t (a) WHERE b > 0, INSERT INTO t VALUES (1, 2, 3) ON CONFLICT (a) WHERE b > 0 DO NOTHING runs, and the second insert is a no-op. ON CONFLICT WHERE b > 0 is a syntax error.
As suggested in review: SQLite's upsert has the same `( indexed-column, ... ) WHERE expr` target, and the parser reads it in every dialect. The doc on `ConflictTarget::Columns` now names both, and a SQLite test pins the parse, so a change that restricted it to PostgreSQL would fail a test rather than only contradict the doc. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dialects: PostgreSQL
Closes #2625.
In PostgreSQL a column-list
ON CONFLICTtarget may end withWHERE index_predicate, which lets a partial unique index be inferred as the arbiter:ConflictTarget::Columnsbecomes a struct variant,Columns { columns: Vec<Ident>, predicate: Option<Expr> }. The predicate is parsed after the column list and beforeDO, and printed back after it.ON CONSTRAINT … WHEREand aWHEREwith no target are still rejected.DO UPDATE SET … WHERE condition(Postgres: [pg on conflict method] does not support 'where' condition. #733, update on conflict with condition #735). A statement can carry both; the tests check that.Exprfield, so visitors see the columns it names, and the target's span covers it.This changes the shape of
ConflictTarget::Columns(it wasColumns(Vec<Ident>)), so code matching on it needs updating. The struct variant also leaves room for the( index_expression )form requested in #1977.