Generate a SQL INSERT statement with bind parameters directly from a Go struct.
- Define column names in struct tags.
- Struct values become bind arguments.
- Use SQL outputs and Args slice piecemeal. Or, use
Insert()
/InsertContext()
with asql.Conn
,sql.DB
, orsql.Tx
to execute the INSERT statement directly. - Works seamlessly with Go standard library database/sql package.
- Supports bind parameter token types of MySQL, PostgreSQL, Oracle, SingleStore (MemSQL), SQL Server (T-SQL), and their equivalents.
- Supports custom struct tags and token types.
- Supports Go 1.8 to 1.19.
- Test coverage: 100% files, 97.5% statements. Tested on Go 1.15, 1.17, and 1.18.
CREATE TABLE candy (
id CHAR(36) NOT NULL
candy_name VARCHAR(255) NOT NULL
form_factor VARCHAR(255) NOT NULL
description VARCHAR(255) NOT NULL
manufacturer VARCHAR(255) NOT NULL
weight_grams DECIMAL(9, 3) NOT NULL
ts DATETIME NOT NULL
)
type CandyInsert struct {
Id string `col:"id"`
Name string `col:"candy_name"`
FormFactor string `col:"form_factor"`
Description string `col:"description"`
Mfr string `col:"manufacturer"`
Weight float64 `col:"weight_grams"`
Timestamp time.Time `col:"ts"`
}
var rec = CandyInsert{
Id: `c0600afd-78a7-4a1a-87c5-1bc48cafd14e`,
Name: `Gougat`,
FormFactor: `Package`,
Description: `tastes like gopher feed`,
Mfr: `Gouggle`,
Weight: 1.16180,
Timestamp: time.Time{},
}
stmt, _ := db.Prepare(`INSERT INTO candy
(id, candy_name, form_factor, description, manufacturer, weight_grams, ts)
VALUES (?, ?, ?, ?, ?, ?, ?)`)
_, err := stmt.Exec(candyInsert.Id, candyInsert.Name, candyInsert.FormFactor,
candyInsert.Description, candyInsert.Mfr, candyInsert.Weight, candyInsert.Timestamp)
ins := sqlinsert.Insert{`candy`, &rec}
_, err := ins.Insert(db)
Question-mark (?) VALUES-tokens are the default:
fmt.Println(ins.SQL())
// INSERT INTO candy (id,candy_name,form_factor,description,manufacturer,weight_grams,ts) VALUES (?,?,?,?,?,?,?)
You can change the token type. For example, for PostgreSQL:
sqlinsert.UseTokenType = OrdinalNumberTokenType
fmt.Println(ins.SQL())
// INSERT INTO candy (id,candy_name,form_factor,description,manufacturer,weight_grams,ts) VALUES ($1,$2,$3,$4,$5,$6,$7)
pretty.Println(ins.Args())
yields (using github.com/kr/pretty):
[]interface {}{
"c0600afd-78a7-4a1a-87c5-1bc48cafd14e",
"Gougat",
"Package",
"tastes like gopher feed",
"Gouggle",
float64(1.1618),
time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
}
stmt, _ := db.Prepare(ins.SQL())
result, _ := stmt.Exec(ins.Args()...)
sqlinsert
is fundamentally a helper for database/sql.
It simply maps struct fields to INSERT elements:
- struct tags
=> SQL columns and tokens
string
=> Preparequery string
- struct values
=> bind args
[]interface{}
=> Execargs ...interface{}
(Go 1.18+args ...any
)
All aspects of SQL INSERT remain in your control:
- I just want the column names for my SQL.
Insert.Columns()
- I just want the parameter-tokens for my SQL.
Insert.Params()
- I just want the bind args for my Exec() call.
Insert.Args()
- I just want a Prepare-Exec wrapper.
Insert.Insert()
Unlike ORMs, sqlinsert
does not create an abstraction layer over SQL relations, nor does it restructure SQL
functions.
The aim is to keep it simple and hide nothing.
SQL’s INSERT is already as close to functionally pure as possible. Why would we change that? Its simplicity and directness are its power.
Some database vendors support collection types for bind parameters, some don’t.
Some database drivers support slices for bind args, some don’t.
The complexity of this reality is met admirably by database/sql
with the necessary amount of flexibility and abstraction:
flexibility in open-ended SQL;
abstraction in the variadic args ...interface{}
for bind args.
In this way, database/sql respects INSERT’s power,
hiding nothing even as it tolerates the vagaries of bind-parameter handling among database vendors and drivers.
Go structs support ordered fields, strong types, and field metadata via tags and
reflection.
In these respects, the Go struct can encapsulate the information of a SQL INSERT-row perfectly and completely.
sqlinsert
uses these features of Go structs to makes your SQL INSERT experience more Go-idiomatic.
Insert.Insert
and Insert.InsertContext
are for simple binding only.
In the spirit of “hide nothing,” these do not support SQL operations in the VALUES
clause.
If you require, say—
INSERT INTO foo (bar, baz, oof) VALUES (some_function(?), REPLACE(?, 'oink', 'moo'), ? + ?);
—then you can use sqlinsert.Insert
methods piecemeal.
For example, use Insert.Columns
to build the column list for Prepare
and Insert.Args
to marshal the args for Exec
/ExecContext
.