Skip to content

Support REPLACE and IGNORE in MySQL :copyfrom Queries#4350

Open
noxymon wants to merge 1 commit intosqlc-dev:mainfrom
noxymon:fix/issue-4339
Open

Support REPLACE and IGNORE in MySQL :copyfrom Queries#4350
noxymon wants to merge 1 commit intosqlc-dev:mainfrom
noxymon:fix/issue-4339

Conversation

@noxymon
Copy link

@noxymon noxymon commented Mar 22, 2026

Context

When using the :copyfrom directive with MySQL in sqlc, users were limited to standard INSERT INTO operations. MySQL supports optimized bulk loading via LOAD DATA LOCAL INFILE, and users often need to handle duplicate keys or errors using the REPLACE or IGNORE modifiers. Previously, even if a user specified REPLACE INTO or INSERT IGNORE INTO in their SQL query, sqlc would translate these to a standard LOAD DATA LOCAL INFILE without the necessary modifiers, leading to potential constraint violations or unintended failures during bulk inserts.

Fix Proposal

This PR introduces support for REPLACE and IGNORE modifiers in :copyfrom queries for the MySQL engine as fixes #4339 . The compiler and codegen have been updated to track these modifiers from the initial SQL AST through to the final Go code generation.

Technical Implementation:

  1. Unified AST Update: Added IsReplace and IgnoreErr fields to the ast.InsertStmt in internal/sql/ast/insert_stmt.go.
  2. MySQL Engine Conversion: Updated the dolphin engine's convertInsertStmt to propagate IsReplace and IgnoreErr from the MySQL-specific parser AST to the unified AST.
  3. Compiler Analysis: Enhanced _analyzeQuery in internal/compiler/analyze.go to extract these flags during semantic analysis.
  4. Plugin Protocol: Updated the plugin.Query message in the codegen protocol to include is_replace and ignore_err fields, ensuring that language-specific generators can access this information.
  5. Go Codegen:
    • Updated the internal query representation in internal/codegen/golang/query.go and result.go.
    • Modified the MySQL copyfrom template (internal/codegen/golang/templates/go-sql-driver-mysql/copyfromCopy.tmpl) to conditionally inject REPLACE or IGNORE into the generated LOAD DATA LOCAL INFILE statement.
  6. End-to-End Testing: Added a new test case mysql_copyfrom_replace to verify that both REPLACE and IGNORE modifiers are correctly generated in the resulting Go code.

Verification Results

  • Verified that REPLACE INTO ... results in LOAD DATA LOCAL INFILE REPLACE ....
  • Verified that INSERT IGNORE INTO ... results in LOAD DATA LOCAL INFILE IGNORE ....
  • All existing and new end-to-end tests passed.

Ensures that REPLACE INTO and INSERT IGNORE INTO statements are correctly
translated to LOAD DATA LOCAL INFILE REPLACE and LOAD DATA LOCAL INFILE IGNORE
respectively when using the :copyfrom directive in MySQL.
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using REPLACE instead of INSERT with :copyfrom in MySQL doesn't add REPLACE to resulting LOAD DATA LOCAL INFILE query

1 participant