Initial commit
This commit is contained in:
792
references/api-reference.md
Normal file
792
references/api-reference.md
Normal file
@@ -0,0 +1,792 @@
|
||||
# cool-mysql API Reference
|
||||
|
||||
Complete API documentation for all cool-mysql methods, organized by category.
|
||||
|
||||
## Database Creation
|
||||
|
||||
### New
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
db, err := mysql.New(
|
||||
"root", "password", "mydb", "localhost", 3306,
|
||||
"root", "password", "mydb", "localhost", 3306,
|
||||
"utf8mb4_unicode_ci",
|
||||
"UTC",
|
||||
)
|
||||
```
|
||||
|
||||
### NewFromDSN
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
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
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
writesConn, _ := sql.Open("mysql", writesDSN)
|
||||
readsConn, _ := sql.Open("mysql", readsDSN)
|
||||
db, err := mysql.NewFromConn(writesConn, readsConn)
|
||||
```
|
||||
|
||||
## Query Methods (SELECT)
|
||||
|
||||
### Select
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
// 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
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
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
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
// 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
|
||||
```go
|
||||
func (db *Database) SelectWritesContext(ctx context.Context, dest any, query string,
|
||||
cacheTTL time.Duration, params ...mysql.Params) error
|
||||
```
|
||||
|
||||
Context-aware version of `SelectWrites()`.
|
||||
|
||||
### SelectJSON
|
||||
```go
|
||||
func (db *Database) SelectJSON(dest *json.RawMessage, query string,
|
||||
cacheTTL time.Duration, params ...mysql.Params) error
|
||||
```
|
||||
|
||||
Select query results as JSON.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
var result json.RawMessage
|
||||
err := db.SelectJSON(&result,
|
||||
"SELECT JSON_OBJECT('id', id, 'name', name) FROM `users` WHERE `id` = @@id",
|
||||
0, 1)
|
||||
```
|
||||
|
||||
### SelectJSONContext
|
||||
```go
|
||||
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
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
count, err := db.Count("SELECT COUNT(*) FROM `users` WHERE `active` = @@active",
|
||||
5*time.Minute, 1)
|
||||
```
|
||||
|
||||
### CountContext
|
||||
```go
|
||||
func (db *Database) CountContext(ctx context.Context, query string, cacheTTL time.Duration,
|
||||
params ...mysql.Params) (int64, error)
|
||||
```
|
||||
|
||||
Context-aware version of `Count()`.
|
||||
|
||||
### Exists
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
exists, err := db.Exists("SELECT 1 FROM `users` WHERE `email` = @@email", 0,
|
||||
"user@example.com")
|
||||
```
|
||||
|
||||
### ExistsContext
|
||||
```go
|
||||
func (db *Database) ExistsContext(ctx context.Context, query string, cacheTTL time.Duration,
|
||||
params ...mysql.Params) (bool, error)
|
||||
```
|
||||
|
||||
Context-aware version of `Exists()`.
|
||||
|
||||
### ExistsWrites
|
||||
```go
|
||||
func (db *Database) ExistsWrites(query string, params ...mysql.Params) (bool, error)
|
||||
```
|
||||
|
||||
Check existence using write pool for read-after-write consistency.
|
||||
|
||||
### ExistsWritesContext
|
||||
```go
|
||||
func (db *Database) ExistsWritesContext(ctx context.Context, query string,
|
||||
params ...mysql.Params) (bool, error)
|
||||
```
|
||||
|
||||
Context-aware version of `ExistsWrites()`.
|
||||
|
||||
## Insert Operations
|
||||
|
||||
### Insert
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
// 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
|
||||
```go
|
||||
func (db *Database) InsertContext(ctx context.Context, table string, data any) error
|
||||
```
|
||||
|
||||
Context-aware version of `Insert()`.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := db.InsertContext(ctx, "users", users)
|
||||
```
|
||||
|
||||
## Upsert Operations
|
||||
|
||||
### Upsert
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
// 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
|
||||
```go
|
||||
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
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
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
|
||||
```go
|
||||
func (db *Database) ExecContext(ctx context.Context, query string, params ...mysql.Params) error
|
||||
```
|
||||
|
||||
Context-aware version of `Exec()`.
|
||||
|
||||
### ExecResult
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
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
|
||||
```go
|
||||
func (db *Database) ExecResultContext(ctx context.Context, query string,
|
||||
params ...mysql.Params) (sql.Result, error)
|
||||
```
|
||||
|
||||
Context-aware version of `ExecResult()`.
|
||||
|
||||
## Transaction Management
|
||||
|
||||
### GetOrCreateTxFromContext
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
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
|
||||
```go
|
||||
func NewContextWithTx(ctx context.Context, tx *sql.Tx) context.Context
|
||||
```
|
||||
|
||||
Store transaction in context for use by database operations.
|
||||
|
||||
### TxFromContext
|
||||
```go
|
||||
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
|
||||
```go
|
||||
func NewContext(ctx context.Context, db *Database) context.Context
|
||||
```
|
||||
|
||||
Store database instance in context.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
ctx := mysql.NewContext(context.Background(), db)
|
||||
```
|
||||
|
||||
### NewContextWithFunc
|
||||
```go
|
||||
func NewContextWithFunc(ctx context.Context, f func() *Database) context.Context
|
||||
```
|
||||
|
||||
Store database factory function in context for lazy initialization.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
ctx := mysql.NewContextWithFunc(ctx, sync.OnceValue(func() *Database {
|
||||
db, err := mysql.New(...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return db
|
||||
}))
|
||||
```
|
||||
|
||||
### FromContext
|
||||
```go
|
||||
func FromContext(ctx context.Context) *Database
|
||||
```
|
||||
|
||||
Retrieve database from context.
|
||||
|
||||
**Returns:**
|
||||
- `*Database` - Database instance or `nil` if not found
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
db := mysql.FromContext(ctx)
|
||||
if db == nil {
|
||||
return errors.New("database not in context")
|
||||
}
|
||||
```
|
||||
|
||||
## Caching Configuration
|
||||
|
||||
### EnableRedis
|
||||
```go
|
||||
func (db *Database) EnableRedis(client *redis.Client)
|
||||
```
|
||||
|
||||
Enable Redis caching with distributed locking.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
redisClient := redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
})
|
||||
db.EnableRedis(redisClient)
|
||||
```
|
||||
|
||||
### EnableMemcache
|
||||
```go
|
||||
func (db *Database) EnableMemcache(client *memcache.Client)
|
||||
```
|
||||
|
||||
Enable Memcached caching.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
memcacheClient := memcache.New("localhost:11211")
|
||||
db.EnableMemcache(memcacheClient)
|
||||
```
|
||||
|
||||
### UseCache
|
||||
```go
|
||||
func (db *Database) UseCache(cache Cache)
|
||||
```
|
||||
|
||||
Use custom cache implementation.
|
||||
|
||||
**Examples:**
|
||||
```go
|
||||
// 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
|
||||
```go
|
||||
func NewWeakCache() *WeakCache
|
||||
```
|
||||
|
||||
Create in-memory cache with weak pointers (GC-managed).
|
||||
|
||||
### NewRedisCache
|
||||
```go
|
||||
func NewRedisCache(client *redis.Client) *RedisCache
|
||||
```
|
||||
|
||||
Create Redis cache with distributed locking support.
|
||||
|
||||
### NewMultiCache
|
||||
```go
|
||||
func NewMultiCache(caches ...Cache) *MultiCache
|
||||
```
|
||||
|
||||
Create layered cache that checks caches in order.
|
||||
|
||||
## Parameter Interpolation
|
||||
|
||||
### InterpolateParams
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
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
|
||||
```go
|
||||
func (db *Database) AddTemplateFuncs(funcs template.FuncMap)
|
||||
```
|
||||
|
||||
Add custom functions available in query templates.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
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
|
||||
```go
|
||||
type Params map[string]any
|
||||
```
|
||||
|
||||
Parameter map for query placeholders.
|
||||
|
||||
### Raw
|
||||
```go
|
||||
type Raw string
|
||||
```
|
||||
|
||||
Literal SQL that won't be escaped. **Use with caution - SQL injection risk.**
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
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
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
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
|
||||
```go
|
||||
type Zeroer interface {
|
||||
IsZero() bool
|
||||
}
|
||||
```
|
||||
|
||||
Implement for custom zero-value detection with `defaultzero` tag.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
type CustomTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (ct CustomTime) IsZero() bool {
|
||||
return ct.Time.IsZero() || ct.Time.Unix() == 0
|
||||
}
|
||||
```
|
||||
|
||||
### Valueser
|
||||
```go
|
||||
type Valueser interface {
|
||||
Values() []any
|
||||
}
|
||||
```
|
||||
|
||||
Implement for custom value conversion during inserts.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
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:**
|
||||
```go
|
||||
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 []
|
||||
```
|
||||
Reference in New Issue
Block a user