Introduction
The sqlx library extends Go's database/sql with named query support, allowing struct fields to be used as query parameters. When struct tags do not match query parameter names, pointer fields are nil, or IN clauses are not handled correctly, sqlx returns missing destination name, could not find name errors, or silently binds wrong values. The named query system uses reflection to map struct fields to SQL parameters, which means tag mismatches and type incompatibilities are only detected at runtime.
Symptoms
missing destination name status in *models.UserOr:
could not find name created_at in UserOr silent wrong values:
// Query uses :user_id but struct has field ID with tag `db:"id"`
// Parameter not bound, query uses NULLCommon Causes
- Struct tag does not match query param:
db:"user_id"in struct but:userIdin query - Unexported struct field: sqlx cannot access lowercase fields
- Nil pointer field: Pointer field is nil, sqlx cannot determine type for binding
- IN clause not handled:
:idswith slice requires specialsqlx.Inhandling - Embedding conflict: Embedded struct has conflicting db tag
- Time zone not set: time.Time serialized with wrong timezone
Step-by-Step Fix
Step 1: Match struct tags to query parameters
go
type User struct {
ID int64 db:"id"
Name string db:"name"
Email string db:"email"
Status string db:"status"
CreatedAt time.Time db:"created_at"`
}
// Query parameter names MUST match db tags
query := SELECT id, name, email, status, created_at FROM users WHERE status = :status
result, err := db.NamedQuery(query, map[string]interface{}{ "status": "active", }) ```
Step 2: Handle nil pointer fields
go
type NullableUser struct {
ID int64 db:"id"
Name *string db:"name" // Can be nil
Email string db:"email"
Age *int db:"age" // Can be nil
UpdatedAt *time.Time db:"updated_at"` // Can be nil
}
// Insert with nullable fields user := NullableUser{ Name: nil, // Will bind as NULL Email: "test@example.com", Age: nil, }
query := INSERT INTO users (name, email, age) VALUES (:name, :email, :age)
_, err := db.NamedExec(query, user)
// sqlx correctly handles nil pointers as SQL NULL
```
Step 3: Handle IN clauses with sqlx.In
```go import "github.com/jmoiron/sqlx"
func getUsersByIDs(db *sqlx.DB, ids []int64) ([]User, error) {
query, args, err := sqlx.In(
SELECT id, name, email FROM users WHERE id IN (?),
ids,
)
if err != nil {
return nil, err
}
// For NamedQuery with IN, use regular Queryx query = db.Rebind(query) // Convert ? to database-specific placeholders rows, err := db.Queryx(query, args...) if err != nil { return nil, err } defer rows.Close()
var users []User for rows.Next() { var u User if err := rows.StructScan(&u); err != nil { return nil, err } users = append(users, u) } return users, nil } ```
Prevention
- Keep struct
db:"..."tags in sync with SQL column names - Use pointer types for fields that can be NULL in the database
- Use
sqlx.Infor IN clauses -- named queries do not handle slices automatically - Use
db.Rebind()to convert ? placeholders to database-specific format - Enable sqlx debug logging to see actual query and parameter values
- Add struct tag validation in tests using reflection
- Use
NamedExecwith map[string]interface{} for dynamic query building