19 KiB
cool-mysql API Reference
Complete API documentation for all cool-mysql methods, organized by category.
Database Creation
New
func New(wUser, wPass, wSchema, wHost string, wPort int,
rUser, rPass, rSchema, rHost string, rPort int,
collation, timeZone string) (*Database, error)
Create a new database connection from connection parameters.
Parameters:
wUser,wPass,wSchema,wHost,wPort- Write connection credentialsrUser,rPass,rSchema,rHost,rPort- Read connection credentialscollation- Database collation (e.g.,"utf8mb4_unicode_ci")timeZone- Time zone for connections (e.g.,"America/New_York","UTC")
Returns:
*Database- Database instance with dual connection poolserror- Connection error if unable to establish connections
Example:
db, err := mysql.New(
"root", "password", "mydb", "localhost", 3306,
"root", "password", "mydb", "localhost", 3306,
"utf8mb4_unicode_ci",
"UTC",
)
NewFromDSN
func NewFromDSN(writesDSN, readsDSN string) (*Database, error)
Create database connection from DSN strings.
Parameters:
writesDSN- Write connection DSNreadsDSN- Read connection DSN
DSN Format:
username:password@protocol(address)/dbname?param=value
Example:
writesDSN := "user:pass@tcp(write-host:3306)/dbname?parseTime=true&loc=UTC"
readsDSN := "user:pass@tcp(read-host:3306)/dbname?parseTime=true&loc=UTC"
db, err := mysql.NewFromDSN(writesDSN, readsDSN)
NewFromConn
func NewFromConn(writesConn, readsConn *sql.DB) (*Database, error)
Create database from existing *sql.DB connections.
Parameters:
writesConn- Existing write connectionreadsConn- Existing read connection
Example:
writesConn, _ := sql.Open("mysql", writesDSN)
readsConn, _ := sql.Open("mysql", readsDSN)
db, err := mysql.NewFromConn(writesConn, readsConn)
Query Methods (SELECT)
Select
func (db *Database) Select(dest any, query string, cacheTTL time.Duration, params ...mysql.Params) error
Execute SELECT query and scan results into destination. Uses read connection pool.
Parameters:
dest- Destination for results (struct, slice, map, channel, function, or primitive)query- SQL query with@@paramNameplaceholderscacheTTL- Cache duration (0= no cache,> 0= cache for duration)params- Query parameters (mysql.Params{}or structs)
Destination Types:
*[]StructType- Slice of structs*StructType- Single struct (returnssql.ErrNoRowsif not found)*string,*int,*time.Time, etc. - Single valuechan StructType- Channel for streaming resultsfunc(StructType)- Function called for each row*[]map[string]any- Slice of maps*json.RawMessage- JSON result
Returns:
error- Query error orsql.ErrNoRowsfor single-value queries with no results
Examples:
// Select into struct slice
var users []User
err := db.Select(&users, "SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users` WHERE age > @@minAge", 5*time.Minute,
18)
// Select single value
var name string
err := db.Select(&name, "SELECT `name` FROM `users` WHERE `id` = @@id", 0,
1)
// Select into channel
userCh := make(chan User)
go func() {
defer close(userCh)
db.Select(userCh, "SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users`", 0)
}()
// Select with function
db.Select(func(u User) {
log.Printf("User: %s", u.Name)
}, "SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users`", 0)
SelectContext
func (db *Database) SelectContext(ctx context.Context, dest any, query string,
cacheTTL time.Duration, params ...mysql.Params) error
Context-aware version of Select(). Supports cancellation and deadlines.
Parameters:
ctx- Context for cancellation/timeout- Additional parameters same as
Select()
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var users []User
err := db.SelectContext(ctx, &users, "SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users`", 0)
SelectWrites
func (db *Database) SelectWrites(dest any, query string, cacheTTL time.Duration,
params ...mysql.Params) error
Select using write connection pool. Use for read-after-write consistency.
When to Use:
- Immediately after INSERT/UPDATE/DELETE when you need to read the modified data
- When you need strong consistency and can't risk reading stale replica data
Example:
// Insert then immediately read
db.Insert("users", user)
db.SelectWrites(&user, "SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users` WHERE `id` = @@id", 0,
user.ID)
SelectWritesContext
func (db *Database) SelectWritesContext(ctx context.Context, dest any, query string,
cacheTTL time.Duration, params ...mysql.Params) error
Context-aware version of SelectWrites().
SelectJSON
func (db *Database) SelectJSON(dest *json.RawMessage, query string,
cacheTTL time.Duration, params ...mysql.Params) error
Select query results as JSON.
Example:
var result json.RawMessage
err := db.SelectJSON(&result,
"SELECT JSON_OBJECT('id', id, 'name', name) FROM `users` WHERE `id` = @@id",
0, 1)
SelectJSONContext
func (db *Database) SelectJSONContext(ctx context.Context, dest *json.RawMessage,
query string, cacheTTL time.Duration,
params ...mysql.Params) error
Context-aware version of SelectJSON().
Utility Query Methods
Count
func (db *Database) Count(query string, cacheTTL time.Duration, params ...mysql.Params) (int64, error)
Execute COUNT query and return result as int64. Uses read pool.
Parameters:
query- Query that returns a single integer (typicallySELECT COUNT(*))cacheTTL- Cache durationparams- Query parameters
Returns:
int64- Count resulterror- Query error
Example:
count, err := db.Count("SELECT COUNT(*) FROM `users` WHERE `active` = @@active",
5*time.Minute, 1)
CountContext
func (db *Database) CountContext(ctx context.Context, query string, cacheTTL time.Duration,
params ...mysql.Params) (int64, error)
Context-aware version of Count().
Exists
func (db *Database) Exists(query string, cacheTTL time.Duration, params ...mysql.Params) (bool, error)
Check if query returns any rows. Uses read pool.
Parameters:
query- Query to check (typicallySELECT 1 FROM ... WHERE ...)cacheTTL- Cache durationparams- Query parameters
Returns:
bool-trueif rows exist,falseotherwiseerror- Query error
Example:
exists, err := db.Exists("SELECT 1 FROM `users` WHERE `email` = @@email", 0,
"user@example.com")
ExistsContext
func (db *Database) ExistsContext(ctx context.Context, query string, cacheTTL time.Duration,
params ...mysql.Params) (bool, error)
Context-aware version of Exists().
ExistsWrites
func (db *Database) ExistsWrites(query string, params ...mysql.Params) (bool, error)
Check existence using write pool for read-after-write consistency.
ExistsWritesContext
func (db *Database) ExistsWritesContext(ctx context.Context, query string,
params ...mysql.Params) (bool, error)
Context-aware version of ExistsWrites().
Insert Operations
Insert
func (db *Database) Insert(table string, data any) error
Insert data into table. Automatically chunks large batches based on max_allowed_packet.
Parameters:
table- Table namedata- Single struct, slice of structs, or channel of structs
Returns:
error- Insert error
Examples:
// Single insert
user := User{Name: "Alice", Email: "alice@example.com"}
err := db.Insert("users", user)
// Batch insert
users := []User{
{Name: "Bob", Email: "bob@example.com"},
{Name: "Charlie", Email: "charlie@example.com"},
}
err := db.Insert("users", users)
// Streaming insert
userCh := make(chan User)
go func() {
for _, u := range users {
userCh <- u
}
close(userCh)
}()
err := db.Insert("users", userCh)
InsertContext
func (db *Database) InsertContext(ctx context.Context, table string, data any) error
Context-aware version of Insert().
Example:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
err := db.InsertContext(ctx, "users", users)
Upsert Operations
Upsert
func (db *Database) Upsert(table string, uniqueCols, updateCols []string,
where string, data any) error
Perform INSERT ... ON DUPLICATE KEY UPDATE operation.
Parameters:
table- Table nameuniqueCols- Columns that define uniqueness (used in conflict detection)updateCols- Columns to update on duplicate keywhere- Optional WHERE clause for conditional update (can be empty)data- Single struct, slice of structs, or channel of structs
Returns:
error- Upsert error
Examples:
// Basic upsert on unique email
err := db.Upsert(
"users",
[]string{"email"}, // unique column
[]string{"name", "updated_at"}, // columns to update
"", // no WHERE clause
user,
)
// Upsert with conditional update
err := db.Upsert(
"users",
[]string{"id"},
[]string{"name", "email"},
"updated_at < VALUES(updated_at)", // only update if newer
users,
)
// Batch upsert
err := db.Upsert(
"users",
[]string{"email"},
[]string{"name", "last_login"},
"",
[]User{{Email: "a@example.com", Name: "Alice"}, ...},
)
UpsertContext
func (db *Database) UpsertContext(ctx context.Context, table string, uniqueCols,
updateCols []string, where string, data any) error
Context-aware version of Upsert().
Execute Operations
Exec
func (db *Database) Exec(query string, params ...mysql.Params) error
Execute query without returning results (UPDATE, DELETE, etc.). Uses write pool.
Parameters:
query- SQL query with@@paramNameplaceholdersparams- Query parameters
Returns:
error- Execution error
Example:
err := db.Exec("UPDATE `users` SET `active` = @@active WHERE `id` = @@id",
mysql.Params{"active": 1, "id": 123})
err := db.Exec("DELETE FROM `users` WHERE last_login < @@cutoff",
time.Now().Add(-365*24*time.Hour))
ExecContext
func (db *Database) ExecContext(ctx context.Context, query string, params ...mysql.Params) error
Context-aware version of Exec().
ExecResult
func (db *Database) ExecResult(query string, params ...mysql.Params) (sql.Result, error)
Execute query and return sql.Result for accessing LastInsertId() and RowsAffected().
Returns:
sql.Result- Execution resulterror- Execution error
Example:
result, err := db.ExecResult("UPDATE `users` SET `name` = @@name WHERE `id` = @@id",
mysql.Params{"name": "Alice", "id": 1})
if err != nil {
return err
}
rowsAffected, _ := result.RowsAffected()
log.Printf("Updated %d rows", rowsAffected)
ExecResultContext
func (db *Database) ExecResultContext(ctx context.Context, query string,
params ...mysql.Params) (sql.Result, error)
Context-aware version of ExecResult().
Transaction Management
GetOrCreateTxFromContext
func GetOrCreateTxFromContext(ctx context.Context) (*sql.Tx, func() error, func(), error)
Get existing transaction from context or create new one.
Returns:
*sql.Tx- Transaction instancefunc() error- Commit functionfunc()- Cancel function (rolls back if not committed)error- Transaction creation error
Usage Pattern:
tx, commit, cancel, err := mysql.GetOrCreateTxFromContext(ctx)
defer cancel() // Always safe to call - rolls back if commit() not called
if err != nil {
return err
}
// Store transaction in context
ctx = mysql.NewContextWithTx(ctx, tx)
// Do database operations...
if err := commit(); err != nil {
return err
}
NewContextWithTx
func NewContextWithTx(ctx context.Context, tx *sql.Tx) context.Context
Store transaction in context for use by database operations.
TxFromContext
func TxFromContext(ctx context.Context) (*sql.Tx, bool)
Retrieve transaction from context.
Returns:
*sql.Tx- Transaction if presentbool-trueif transaction exists in context
Context Management
NewContext
func NewContext(ctx context.Context, db *Database) context.Context
Store database instance in context.
Example:
ctx := mysql.NewContext(context.Background(), db)
NewContextWithFunc
func NewContextWithFunc(ctx context.Context, f func() *Database) context.Context
Store database factory function in context for lazy initialization.
Example:
ctx := mysql.NewContextWithFunc(ctx, sync.OnceValue(func() *Database {
db, err := mysql.New(...)
if err != nil {
panic(err)
}
return db
}))
FromContext
func FromContext(ctx context.Context) *Database
Retrieve database from context.
Returns:
*Database- Database instance ornilif not found
Example:
db := mysql.FromContext(ctx)
if db == nil {
return errors.New("database not in context")
}
Caching Configuration
EnableRedis
func (db *Database) EnableRedis(client *redis.Client)
Enable Redis caching with distributed locking.
Example:
redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
db.EnableRedis(redisClient)
EnableMemcache
func (db *Database) EnableMemcache(client *memcache.Client)
Enable Memcached caching.
Example:
memcacheClient := memcache.New("localhost:11211")
db.EnableMemcache(memcacheClient)
UseCache
func (db *Database) UseCache(cache Cache)
Use custom cache implementation.
Examples:
// In-memory cache
db.UseCache(mysql.NewWeakCache())
// Multi-level cache
db.UseCache(mysql.NewMultiCache(
mysql.NewWeakCache(), // L1: Local fast cache
mysql.NewRedisCache(redisClient), // L2: Distributed cache
))
NewWeakCache
func NewWeakCache() *WeakCache
Create in-memory cache with weak pointers (GC-managed).
NewRedisCache
func NewRedisCache(client *redis.Client) *RedisCache
Create Redis cache with distributed locking support.
NewMultiCache
func NewMultiCache(caches ...Cache) *MultiCache
Create layered cache that checks caches in order.
Parameter Interpolation
InterpolateParams
func (db *Database) InterpolateParams(query string, params ...mysql.Params) (string, []any, error)
Manually interpolate parameters in query. Useful for debugging or logging.
Parameters:
query- Query with@@paramNameplaceholdersparams- Parameters to interpolate
Returns:
string- Query with?placeholders[]any- Normalized parameter valueserror- Interpolation error
Example:
replacedQuery, normalizedParams, err := db.InterpolateParams(
"SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users` WHERE `id` = @@id",
mysql.Params{"id": 1},
)
// replacedQuery: "SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users` WHERE `id` = ?"
// normalizedParams: []any{1}
Template Functions
AddTemplateFuncs
func (db *Database) AddTemplateFuncs(funcs template.FuncMap)
Add custom functions available in query templates.
Example:
db.AddTemplateFuncs(template.FuncMap{
"upper": strings.ToUpper,
"lower": strings.ToLower,
})
db.Select(&users,
"SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users` WHERE `name` = @@name{{ if .UpperCase }} COLLATE utf8mb4_bin{{ end }}",
0,
mysql.Params{"name": "alice", "upperCase": true})
Special Types
Params
type Params map[string]any
Parameter map for query placeholders.
Raw
type Raw string
Literal SQL that won't be escaped. Use with caution - SQL injection risk.
Example:
db.Select(&users, "SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users` WHERE @@condition", 0,
mysql.Params{
"condition": mysql.Raw("created_at > NOW() - INTERVAL 1 DAY"),
})
MapRow / SliceRow / MapRows / SliceRows
type MapRow map[string]any
type SliceRow []any
type MapRows []map[string]any
type SliceRows [][]any
Flexible result types when struct mapping isn't needed.
Example:
var rows mysql.MapRows
db.Select(&rows, "SELECT `id`, name FROM `users`", 0)
for _, row := range rows {
fmt.Printf("ID: %v, Name: %v\n", row["id"], row["name"])
}
Custom Interfaces
Zeroer
type Zeroer interface {
IsZero() bool
}
Implement for custom zero-value detection with defaultzero tag.
Example:
type CustomTime struct {
time.Time
}
func (ct CustomTime) IsZero() bool {
return ct.Time.IsZero() || ct.Time.Unix() == 0
}
Valueser
type Valueser interface {
Values() []any
}
Implement for custom value conversion during inserts.
Example:
type Point struct {
X, Y float64
}
func (p Point) Values() []any {
return []any{p.X, p.Y}
}
Error Handling
Automatic Retries
cool-mysql automatically retries these MySQL error codes:
1213- Deadlock detected1205- Lock wait timeout exceeded2006- MySQL server has gone away2013- Lost connection to MySQL server during query
Retry behavior uses exponential backoff and can be configured with COOL_MAX_ATTEMPTS environment variable.
sql.ErrNoRows
- Single value/struct queries: Returns
sql.ErrNoRowswhen no results - Slice queries: Returns empty slice (not
sql.ErrNoRows)
Example:
var name string
err := db.Select(&name, "SELECT `name` FROM `users` WHERE `id` = @@id", 0,
999)
if errors.Is(err, sql.ErrNoRows) {
// Handle not found
}
var users []User
err := db.Select(&users, "SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users` WHERE `id` = @@id", 0,
999)
// err is nil, users is empty slice []