diff --git a/internal/codegen/golang/templates/go-sql-driver-mysql/copyfromCopy.tmpl b/internal/codegen/golang/templates/go-sql-driver-mysql/copyfromCopy.tmpl index 3bdc5d5729..e6b9061d31 100644 --- a/internal/codegen/golang/templates/go-sql-driver-mysql/copyfromCopy.tmpl +++ b/internal/codegen/golang/templates/go-sql-driver-mysql/copyfromCopy.tmpl @@ -10,7 +10,7 @@ func convertRowsFor{{.MethodName}}(w *io.PipeWriter, {{.Arg.SlicePair}}) { {{- range $arg.CopyFromMySQLFields}} {{- if eq .Type "string"}} e.AppendString({{if eq (len $arg.CopyFromMySQLFields) 1}}row{{else}}row.{{.Name}}{{end}}) -{{- else if eq .Type "[]byte"}} +{{- else if or (eq .Type "[]byte") (eq .Type "json.RawMessage")}} e.AppendBytes({{if eq (len $arg.CopyFromMySQLFields) 1}}row{{else}}row.{{.Name}}{{end}}) {{- else}} e.AppendValue({{if eq (len $arg.CopyFromMySQLFields) 1}}row{{else}}row.{{.Name}}{{end}}) diff --git a/internal/endtoend/testdata/json/mysql/go/copyfrom.go b/internal/endtoend/testdata/json/mysql/go/copyfrom.go new file mode 100644 index 0000000000..ca4140873c --- /dev/null +++ b/internal/endtoend/testdata/json/mysql/go/copyfrom.go @@ -0,0 +1,51 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.24.0 +// source: copyfrom.go + +package querytest + +import ( + "context" + "fmt" + "io" + "sync/atomic" + + "github.com/go-sql-driver/mysql" + "github.com/hexon/mysqltsv" +) + +var readerHandlerSequenceForBulkInsert uint32 = 1 + +func convertRowsForBulkInsert(w *io.PipeWriter, arg []BulkInsertParams) { + e := mysqltsv.NewEncoder(w, 2, nil) + for _, row := range arg { + e.AppendBytes(row.A) + e.AppendBytes(row.B) + } + w.CloseWithError(e.Close()) +} + +// BulkInsert 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. +// +// Check the documentation for more information: +// https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-error-handling +func (q *Queries) BulkInsert(ctx context.Context, arg []BulkInsertParams) (int64, error) { + pr, pw := io.Pipe() + defer pr.Close() + rh := fmt.Sprintf("BulkInsert_%d", atomic.AddUint32(&readerHandlerSequenceForBulkInsert, 1)) + mysql.RegisterReaderHandler(rh, func() io.Reader { return pr }) + defer mysql.DeregisterReaderHandler(rh) + go convertRowsForBulkInsert(pw, arg) + // The string interpolation is necessary because LOAD DATA INFILE requires + // the file name to be given as a literal string. + result, err := q.db.ExecContext(ctx, fmt.Sprintf("LOAD DATA LOCAL INFILE '%s' INTO TABLE `foo` %s (a, b)", "Reader::"+rh, mysqltsv.Escaping)) + if err != nil { + return 0, err + } + return result.RowsAffected() +} diff --git a/internal/endtoend/testdata/json/mysql/go/query.sql.go b/internal/endtoend/testdata/json/mysql/go/query.sql.go index d2897ef807..ca91d84ddb 100644 --- a/internal/endtoend/testdata/json/mysql/go/query.sql.go +++ b/internal/endtoend/testdata/json/mysql/go/query.sql.go @@ -7,8 +7,18 @@ package querytest import ( "context" + "encoding/json" ) +const bulkInsert = `-- name: BulkInsert :copyfrom +INSERT INTO foo (a, b) VALUES (?, ?) +` + +type BulkInsertParams struct { + A json.RawMessage + B json.RawMessage +} + const selectFoo = `-- name: SelectFoo :exec SELECT a, b FROM foo ` diff --git a/internal/endtoend/testdata/json/mysql/query.sql b/internal/endtoend/testdata/json/mysql/query.sql index a8629cc2ae..927e597562 100644 --- a/internal/endtoend/testdata/json/mysql/query.sql +++ b/internal/endtoend/testdata/json/mysql/query.sql @@ -1,2 +1,5 @@ -- name: SelectFoo :exec SELECT * FROM foo; + +-- name: BulkInsert :copyfrom +INSERT INTO foo (a, b) VALUES (?, ?); diff --git a/internal/endtoend/testdata/json/mysql/sqlc.json b/internal/endtoend/testdata/json/mysql/sqlc.json index e41c39e8b3..b1127e200f 100644 --- a/internal/endtoend/testdata/json/mysql/sqlc.json +++ b/internal/endtoend/testdata/json/mysql/sqlc.json @@ -4,6 +4,7 @@ { "path": "go", "engine": "mysql", + "sql_driver": "github.com/go-sql-driver/mysql", "name": "querytest", "schema": "schema.sql", "queries": "query.sql"