forked from sqlc-dev/sqlc
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MySQL LOAD DATA INFILE: First version
This enables the :copyfrom query annotation for people using go-sql-driver/mysql that transforms it into a LOAD DATA LOCAL INFILE. issue sqlc-dev#2179
- Loading branch information
Showing
14 changed files
with
281 additions
and
6 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
60 changes: 60 additions & 0 deletions
60
internal/codegen/golang/templates/go-sql-driver-mysql/copyfromCopy.tmpl
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
{{define "copyfromCodeGoSqlDriver"}} | ||
{{range .GoQueries}} | ||
{{if eq .Cmd ":copyfrom" }} | ||
var readerHandlerSequenceFor{{.MethodName}} uint32 = 1 | ||
|
||
func convertRowsFor{{.MethodName}}(w *io.PipeWriter, {{.Arg.SlicePair}}) { | ||
e := mysqltsv.NewEncoder(w, {{ len .Arg.Fields }}, nil) | ||
for _, row := range {{.Arg.Name}} { | ||
{{- with $arg := .Arg }} | ||
{{- range $arg.Fields}} | ||
{{- if eq .Type "string"}} | ||
e.AppendString({{if eq (len $arg.Fields) 1}}row{{else}}row.{{.Name}}{{end}}) | ||
{{- else if eq .Type "[]byte"}} | ||
e.AppendBytes({{if eq (len $arg.Fields) 1}}row{{else}}row.{{.Name}}{{end}}) | ||
{{- else}} | ||
e.AppendValue({{if eq (len $arg.Fields) 1}}row{{else}}row.{{.Name}}{{end}}) | ||
{{- end}} | ||
{{- end}} | ||
{{- end}} | ||
} | ||
w.CloseWithError(e.Close()) | ||
} | ||
|
||
{{range .Comments}}//{{.}} | ||
{{end -}} | ||
// {{.MethodName}} uses MySQL's LOAD DATA LOCAL INFILE and is not atomic. Errors and duplicate keys are treated as warnings and insertion will continue, even without an error for some cases. | ||
// Use this in a transaction and use SHOW WARNINGS to check for any problems and roll back if you want to. | ||
// This is a MySQL limitation, not sqlc. Check the documentation for more information: https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-error-handling | ||
{{- if $.EmitMethodsWithDBArgument}} | ||
func (q *Queries) {{.MethodName}}(ctx context.Context, db DBTX, {{.Arg.SlicePair}}) (int64, error) { | ||
pr, pw := io.Pipe() | ||
defer pr.Close() | ||
rh := fmt.Sprintf("{{.MethodName}}_%d", atomic.AddUint32(&readerHandlerSequenceFor{{.MethodName}}, 1)) | ||
mysql.RegisterReaderHandler(rh, func() io.Reader { return pr }) | ||
defer mysql.DeregisterReaderHandler(rh) | ||
go convertRowsFor{{.MethodName}}(pw, {{.Arg.Name}}) | ||
result, err := 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)) | ||
if err != nil { | ||
return 0, err | ||
} | ||
return result.RowsAffected() | ||
{{- else}} | ||
func (q *Queries) {{.MethodName}}(ctx context.Context, {{.Arg.SlicePair}}) (int64, error) { | ||
pr, pw := io.Pipe() | ||
defer pr.Close() | ||
rh := fmt.Sprintf("{{.MethodName}}_%d", atomic.AddUint32(&readerHandlerSequenceFor{{.MethodName}}, 1)) | ||
mysql.RegisterReaderHandler(rh, func() io.Reader { return pr }) | ||
defer mysql.DeregisterReaderHandler(rh) | ||
go convertRowsFor{{.MethodName}}(pw, {{.Arg.Name}}) | ||
result, err := q.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)) | ||
if err != nil { | ||
return 0, err | ||
} | ||
return result.RowsAffected() | ||
{{- end}} | ||
} | ||
|
||
{{end}} | ||
{{end}} | ||
{{end}} |
This file contains 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
This file contains 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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
CREATE TABLE foo (a text, b integer, c DATETIME, d DATE); | ||
|
||
-- name: InsertValues :copyfrom | ||
INSERT INTO foo (a, b, c, d) VALUES (?, ?, ?, ?); | ||
|
||
-- name: InsertSingleValue :copyfrom | ||
INSERT INTO foo (a) VALUES (?); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"version": "1", | ||
"packages": [ | ||
{ | ||
"path": "go", | ||
"sql_package": "database/sql", | ||
"sql_driver": "github.com/go-sql-driver/mysql", | ||
"engine": "mysql", | ||
"name": "querytest", | ||
"schema": "query.sql", | ||
"queries": "query.sql" | ||
} | ||
] | ||
} |
This file contains 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
This file contains 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