Skip to content

Commit 2ccb46f

Browse files
author
Pickle Rick
committed
feat(mysql): support REPLACE and IGNORE in :copyfrom queries
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.
1 parent ce83d3f commit 2ccb46f

File tree

17 files changed

+241
-2
lines changed

17 files changed

+241
-2
lines changed

internal/cmd/shim.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ func pluginQueries(r *compiler.Result) []*plugin.Query {
161161
Params: params,
162162
Filename: q.Metadata.Filename,
163163
InsertIntoTable: iit,
164+
IsReplace: q.IsReplace,
165+
IgnoreErr: q.IgnoreErr,
164166
})
165167
}
166168
return out

internal/codegen/golang/query.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,9 @@ type Query struct {
266266
Ret QueryValue
267267
Arg QueryValue
268268
// Used for :copyfrom
269-
Table *plugin.Identifier
269+
Table *plugin.Identifier
270+
IsReplace bool
271+
IgnoreErr bool
270272
}
271273

272274
func (q Query) hasRetType() bool {

internal/codegen/golang/result.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ func buildQueries(req *plugin.GenerateRequest, options *opts.Options, structs []
223223
SQL: query.Text,
224224
Comments: comments,
225225
Table: query.InsertIntoTable,
226+
IsReplace: query.IsReplace,
227+
IgnoreErr: query.IgnoreErr,
226228
}
227229
sqlpkg := parseDriver(options.SqlPackage)
228230

internal/codegen/golang/templates/go-sql-driver-mysql/copyfromCopy.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context{{if $.EmitMethodsWithDBArg
4040
go convertRowsFor{{.MethodName}}(pw, {{.Arg.Name}})
4141
// The string interpolation is necessary because LOAD DATA INFILE requires
4242
// the file name to be given as a literal string.
43-
result, err := {{if (not $.EmitMethodsWithDBArgument)}}q.{{end}}db.ExecContext(ctx, fmt.Sprintf("LOAD DATA LOCAL INFILE '%s' INTO TABLE {{.TableIdentifierForMySQL}} %s ({{range $index, $name := .Arg.ColumnNames}}{{if gt $index 0}}, {{end}}{{$name}}{{end}})", "Reader::" + rh, mysqltsv.Escaping))
43+
result, err := {{if (not $.EmitMethodsWithDBArgument)}}q.{{end}}db.ExecContext(ctx, fmt.Sprintf("LOAD DATA LOCAL INFILE '%s' {{if .IsReplace}}REPLACE {{else if .IgnoreErr}}IGNORE {{end}}INTO TABLE {{.TableIdentifierForMySQL}} %s ({{range $index, $name := .Arg.ColumnNames}}{{if gt $index 0}}, {{end}}{{$name}}{{end}})", "Reader::" + rh, mysqltsv.Escaping))
4444
if err != nil {
4545
return 0, err
4646
}

internal/compiler/analyze.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414

1515
type analysis struct {
1616
Table *ast.TableName
17+
IsReplace bool
18+
IgnoreErr bool
1719
Columns []*Column
1820
Parameters []Parameter
1921
Named *named.ParamSet
@@ -142,6 +144,8 @@ func (c *Compiler) _analyzeQuery(raw *ast.RawStmt, query string, failfast bool)
142144
raw, namedParams, edits := rewrite.NamedParameters(c.conf.Engine, raw, numbers, dollar)
143145

144146
var table *ast.TableName
147+
var isReplace bool
148+
var ignoreErr bool
145149
switch n := raw.Stmt.(type) {
146150
case *ast.InsertStmt:
147151
if err := check(validate.InsertStmt(n)); err != nil {
@@ -152,6 +156,8 @@ func (c *Compiler) _analyzeQuery(raw *ast.RawStmt, query string, failfast bool)
152156
if err := check(err); err != nil {
153157
return nil, err
154158
}
159+
isReplace = n.IsReplace
160+
ignoreErr = n.IgnoreErr
155161
}
156162

157163
if err := check(validate.FuncCall(c.catalog, c.combo, raw)); err != nil {
@@ -207,6 +213,8 @@ func (c *Compiler) _analyzeQuery(raw *ast.RawStmt, query string, failfast bool)
207213

208214
return &analysis{
209215
Table: table,
216+
IsReplace: isReplace,
217+
IgnoreErr: ignoreErr,
210218
Columns: cols,
211219
Parameters: params,
212220
Query: expanded,

internal/compiler/parse.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ func (c *Compiler) parseQuery(stmt ast.Node, src string, o opts.Parser) (*Query,
178178
Columns: anlys.Columns,
179179
SQL: trimmed,
180180
InsertIntoTable: anlys.Table,
181+
IsReplace: anlys.IsReplace,
182+
IgnoreErr: anlys.IgnoreErr,
181183
}, nil
182184
}
183185

internal/compiler/query.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ type Query struct {
5050

5151
// Needed for CopyFrom
5252
InsertIntoTable *ast.TableName
53+
IsReplace bool
54+
IgnoreErr bool
5355

5456
// Needed for vet
5557
RawStmt *ast.RawStmt

internal/endtoend/testdata/mysql_copyfrom_replace/db/copyfrom.go

Lines changed: 92 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/mysql_copyfrom_replace/db/db.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/mysql_copyfrom_replace/db/models.go

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)