Exec
Execute SQL or CQL statements using Exec, ExecQuery, Insert, Update, and Delete.
The db package provides execution helpers for statements that do not return rows.
Use these helpers for inserts, updates, deletes, schema changes, maintenance queries, and other SQL or CQL statements where the result is represented by db.Result.
Overview
| Function | Purpose |
|---|---|
Exec | Execute a raw SQL or CQL statement |
ExecQuery | Execute a prepared db.Query value |
Insert | Execute an insert statement |
Update | Execute an update statement |
Delete | Execute a delete statement |
Insert, Update, and Delete are semantic wrappers around Exec.
They do not inspect, validate, or rewrite SQL. They exist to make application code easier to read.
Exec
Use Exec when you want to execute a raw statement with arguments.
result, err := db.Exec(ctx, conn, `
UPDATE users
SET active = ?
WHERE id = ?
`, active, id)
if err != nil {
return err
}Exec creates a db.Query internally and passes it to ExecQuery.
Conceptually, this:
result, err := db.Exec(ctx, conn, query, args...)is equivalent to:
q, err := db.Raw(query, args...)
if err != nil {
return nil, err
}
result, err := db.ExecQuery(ctx, conn, q)Use Exec when the operation is generic or when you do not want to label it as insert, update, or delete.
ExecQuery
Use ExecQuery when you already have a db.Query.
q, err := db.Raw(`
UPDATE users
SET active = ?
WHERE id = ?
`, active, id)
if err != nil {
return err
}
result, err := db.ExecQuery(ctx, conn, q)
if err != nil {
return err
}This is useful when queries are built or selected before execution.
For example:
q, err := db.Dialect(conn, queries.UpdateUserStatus, active, id)
if err != nil {
return err
}
result, err := db.ExecQuery(ctx, conn, q)
if err != nil {
return err
}Semantic Helpers
Insert, Update, and Delete call Exec internally.
func Insert(ctx context.Context, c Execer, query string, args ...any) (Result, error) {
return Exec(ctx, c, query, args...)
}
func Update(ctx context.Context, c Execer, query string, args ...any) (Result, error) {
return Exec(ctx, c, query, args...)
}
func Delete(ctx context.Context, c Execer, query string, args ...any) (Result, error) {
return Exec(ctx, c, query, args...)
}These helpers are intentionally simple.
They do not make the package an ORM. They only communicate intent in repository or service code.
Insert
Use Insert when the statement creates new data.
result, err := db.Insert(ctx, conn, `
INSERT INTO users (name, email, active)
VALUES (?, ?, ?)
`, name, email, active)
if err != nil {
return err
}For MySQL, the returned result can expose LastInsertId.
fmt.Println(result.LastInsertId())
fmt.Println(result.RowsAffected())PostgreSQL often uses RETURNING, so when you need the generated ID, db.Value is usually a better fit.
id, found, err := db.Value[int64](ctx, conn, `
INSERT INTO users (name, email, active)
VALUES ($1, $2, $3)
RETURNING id
`, name, email, active)Scylla often generates IDs in application code and writes them explicitly.
Update
Use Update when the statement modifies existing data.
result, err := db.Update(ctx, conn, `
UPDATE users
SET active = ?
WHERE id = ?
`, active, id)
if err != nil {
return err
}
fmt.Println(result.RowsAffected())Update does not validate that the SQL statement is an UPDATE.
It is a readable wrapper around Exec.
Delete
Use Delete when the statement removes data.
result, err := db.Delete(ctx, conn, `
DELETE FROM users
WHERE id = ?
`, id)
if err != nil {
return err
}
fmt.Println(result.RowsAffected())Delete does not validate that the SQL statement is a DELETE.
It is a readable wrapper around Exec.
Result
Execution helpers return db.Result.
type Result interface {
RowsAffected() int64
LastInsertId() int64
}Use RowsAffected to inspect how many rows were changed.
affected := result.RowsAffected()Use LastInsertId when the selected driver supports it.
id := result.LastInsertId()Driver behavior differs:
| Driver | RowsAffected | LastInsertId |
|---|---|---|
| MySQL | supported | supported |
| Postgres | supported | usually 0; prefer RETURNING with Value |
| Scylla | driver-specific | usually not used |
Placeholder Style
Execution helpers do not rewrite placeholders.
Use the placeholder style required by the selected driver.
| Driver | Placeholder style |
|---|---|
| MySQL | ? |
| Postgres | $1, $2, $3 |
| Scylla | ? |
MySQL example:
result, err := db.Update(ctx, conn, `
UPDATE users
SET active = ?
WHERE id = ?
`, active, id)PostgreSQL example:
result, err := db.Update(ctx, conn, `
UPDATE users
SET active = $1
WHERE id = $2
`, active, id)Scylla example:
result, err := db.Update(ctx, conn, `
UPDATE users_by_id
SET active = ?
WHERE id = ?
`, active, id)When to Use Each Helper
| Need | Use |
|---|---|
| Execute any statement | Exec |
Execute an existing db.Query | ExecQuery |
| Communicate insert intent | Insert |
| Communicate update intent | Update |
| Communicate delete intent | Delete |
| Return a value from a statement | Value |
| Execute dialect-selected statement | Dialect + ExecQuery |
Recommended Style
Use semantic helpers when the operation is clear.
result, err := db.Insert(ctx, conn, insertUserQuery, name, email, active)result, err := db.Update(ctx, conn, updateUserQuery, active, id)result, err := db.Delete(ctx, conn, deleteUserQuery, id)Use Exec for generic statements.
result, err := db.Exec(ctx, conn, query, args...)Use ExecQuery when working with db.Query.
q, err := db.Raw(query, args...)
if err != nil {
return err
}
result, err := db.ExecQuery(ctx, conn, q)Not an ORM
These helpers do not generate SQL and do not know what your statement does.
They are small execution helpers over explicit SQL or CQL.
You still control:
- the statement text
- placeholders
- arguments
- transaction boundaries
- driver-specific behavior
- schema and data model design