feat: implement support for INSERT ... ON CONFLICT (DO NOTHING / DO UPDATE) clauses - #24982
Open
Nachiket-Roy wants to merge 3 commits into
Open
feat: implement support for INSERT ... ON CONFLICT (DO NOTHING / DO UPDATE) clauses#24982Nachiket-Roy wants to merge 3 commits into
Nachiket-Roy wants to merge 3 commits into
Conversation
…preserve Base table error prefix
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #24982 +/- ##
==========================================
- Coverage 81.67% 81.65% -0.02%
==========================================
Files 1126 1126
Lines 414533 415171 +638
Branches 414533 415171 +638
==========================================
+ Hits 338562 339025 +463
- Misses 56058 56203 +145
- Partials 19913 19943 +30 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
Rationale for this change
DataFusion previously rejected PostgreSQL-style
INSERT INTO ... ON CONFLICT (col, ...) DO NOTHINGandINSERT INTO ... ON CONFLICT (col, ...) DO UPDATE SET ... [WHERE ...]upsert statements during SQL planning with"This feature is not implemented: ON CONFLICT is not supported".Design & Architectural Approach
Rather than modifying
InsertOpor adding new dispatch methods onTableProvider(which would break external connectors such asdelta-rs,iceberg-rust, and custom catalogs across upgrades) or adding protobuf schema churn, this change desugarsINSERT ... ON CONFLICTintoWriteOp::MergeInto(Box<MergeIntoOp>)at the SQL planning stage:InsertOpremains unchanged (Append,Overwrite,Replace), andTableProvider::insert_intois untouched.dml_node::Type::MergeIntoprotobuf representation.TableProvider::merge_intoautomatically gainsON CONFLICTupsert capabilities for free.excluded.DO NOTHINGmaps toWHEN NOT MATCHED THEN INSERT.DO UPDATEmaps toWHEN MATCHED [AND predicate] THEN UPDATE SET ...followed byWHEN NOT MATCHED THEN INSERT.NULLconflict keys never conflict (NULL != NULL), taking the insert path."ON CONFLICT DO UPDATE command cannot affect row a second time", whileDO NOTHINGcoalesces/deduplicates them.constraints() == Noneor empty): validated against existing schema columns, and guarded against duplicate rows at runtime.What changes are included in this PR?
1. SQL Planning & Desugaring (
datafusion-sql)onconflict clause.ON CONFLICTcannot be combined withINSERT OVERWRITEorREPLACE INTO.excluded.SubqueryAlias("excluded").table_source.constraints()contains defined constraints (!constraints.is_empty()), strictly validates that target columns match aConstraint::PrimaryKeyorConstraint::Unique(failing with"There is no unique or exclusion constraint matching the ON CONFLICT specification"if mismatched). When constraints are empty orNone, allows the operation so real connectors are not blocked.MergeIntoOpwith equality join conditions between target columns andexcludedcolumns.2. MemTable Reference Execution (
datafusion-catalog)TableProvider::merge_intoonMemTablein datafusion/catalog/src/memory/table.rs:0..N-1).HashMap<RowKey, (partition_idx, batch_idx, row_idx)>.DO UPDATEand coalesces duplicates forDO NOTHING.NULLin any conflict column bypass conflict detection and take the insert path.extract_columnto unwrap nestedExpr::AliasandExpr::Castwhen extracting equi-join keys.DmlResultExec.What is the testing strategy for this PR?
End-to-End Sqllogictests:
DO NOTHINGrow skipping.DO UPDATEmodifications with and withoutWHEREpredicates (where excluded.score > users.score).NULLconflict keys bypassing conflict detection (NULL != NULL).DO UPDATEand coalescing onDO NOTHING.(a, b).excluded, non-existent columns, duplicate assignments).MemTable.Unit & Integration Tests:
datafusion/sql/tests/sql_integration.rs: Added integration tests verifying plannedMergeIntoOpstructures, predicates, and aliases forDO NOTHINGandDO UPDATE.datafusion/sql/tests/sql_integration.rs&datafusion/sql/tests/common/mod.rs: Added 6 negative test cases intest_insert_schema_errorschecking mutual exclusivity, non-existent columns, duplicate assignments, and constraint mismatches.Format & Lint:
cargo fmt --all -- --check.cargo clippy -p datafusion-sql -p datafusion-catalog --all-targets --all-features -- -D warningswith zero warnings.Are there any user-facing changes?
INSERT INTO ... ON CONFLICT (...) DO NOTHINGandINSERT INTO ... ON CONFLICT (...) DO UPDATE SET ...statements in SQL.MemTablenow supports executingMERGE INTOqueries in-memory.InsertOpis completely unchanged, and existing connector implementations remain 100% compatible.