Files
gh-stirlingmarketinggroup-c…/references/api-reference.md
2025-11-30 08:58:35 +08:00

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 credentials
  • rUser, rPass, rSchema, rHost, rPort - Read connection credentials
  • collation - 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 pools
  • error - 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 DSN
  • readsDSN - 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 connection
  • readsConn - 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 @@paramName placeholders
  • cacheTTL - 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 (returns sql.ErrNoRows if not found)
  • *string, *int, *time.Time, etc. - Single value
  • chan StructType - Channel for streaming results
  • func(StructType) - Function called for each row
  • *[]map[string]any - Slice of maps
  • *json.RawMessage - JSON result

Returns:

  • error - Query error or sql.ErrNoRows for 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 (typically SELECT COUNT(*))
  • cacheTTL - Cache duration
  • params - Query parameters

Returns:

  • int64 - Count result
  • error - 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 (typically SELECT 1 FROM ... WHERE ...)
  • cacheTTL - Cache duration
  • params - Query parameters

Returns:

  • bool - true if rows exist, false otherwise
  • error - 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 name
  • data - 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 name
  • uniqueCols - Columns that define uniqueness (used in conflict detection)
  • updateCols - Columns to update on duplicate key
  • where - 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 @@paramName placeholders
  • params - 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 result
  • error - 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 instance
  • func() error - Commit function
  • func() - 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 present
  • bool - true if 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 or nil if 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 @@paramName placeholders
  • params - Parameters to interpolate

Returns:

  • string - Query with ? placeholders
  • []any - Normalized parameter values
  • error - 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 detected
  • 1205 - Lock wait timeout exceeded
  • 2006 - MySQL server has gone away
  • 2013 - 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.ErrNoRows when 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 []