<< All versions
Skill v1.0.0
currentLLM-judged scan90/100alex123098/my-dotfiles/go-database
──Details
PublishedOctober 24, 2026 at 03:28 AM
Content Hashsha256:2ebb442c8c70c615...
Git SHA79838ab4898e
──Files
Files (1 file, 3.5 KB)
SKILL.md3.5 KBactive
SKILL.md · 30 lines · 3.5 KB
name: "go-database" description: "Go database access patterns — parameterized queries, struct scanning, transactions, connection pooling, context propagation. Use when writing, reviewing, or debugging Go code that interacts with PostgreSQL, MySQL, or SQLite." version: 1 created: "2026-06-20" updated: "2026-06-20"
Procedure
- Use sqlx or pgx, Not ORMs: Use
jmoiron/sqlxfor struct scanning ergonomics orjackc/pgxfor PostgreSQL-specific features. Avoid GORM and other ORMs — they hide SQL, generate unpredictable queries, and make debugging harder. ORMs also obscure JOINs, subqueries, and index utilization from query plans. - Parameterized Queries: NEVER concatenate user input into SQL strings — use
?(MySQL/SQLite) or$1(PostgreSQL) placeholders:db.GetContext(ctx, &user, "SELECT * FROM users WHERE id = ?", id). Parameterization is not optional — concatenation leads to SQL injection. - Scanning & NULL Handling: Use sqlx
StructScan/Get/Selectfor automatic struct mapping. Handle NULLable columns with*string,*intpointers orsql.NullXxxtypes. UseCOALESCEin SQL as a simpler alternative. Always handlesql.ErrNoRowsexplicitly — distinguish 'not found' from real errors usingerrors.Is(err, sql.ErrNoRows). - Transactions: Use
BeginTxx/BeginTxfor multi-statement operations. Handle commit/rollback viadeferpattern:tx, err := db.BeginTxx(ctx, nil); if err != nil { return err }; defer tx.Rollback()— thentx.Commit()at the end. Set isolation levels when default READ COMMITTED is insufficient (SERIALIZABLE for financial operations). UseSELECT ... FOR UPDATEwhen reading data to modify — prevents race conditions. - Connection Pool: Always configure pool limits:
db.SetMaxOpenConns(n)(limit total connections),db.SetMaxIdleConns(n)(keep warm connections),db.SetConnMaxLifetime(d)(recycle connections),db.SetConnMaxIdleTime(d)(close idle connections). Default MaxIdleConns is 2 — too low for any concurrent service. Match MaxOpenConns to your DB's configured connection limit. - Context & Batch Patterns: Always use
*Contextmethod variants (QueryContext,ExecContext,GetContext) to respect request deadlines. Alwaysdefer rows.Close()immediately afterQueryContextcall. Usedb.Exec(notQuery) for statements returning no rows —Queryreturns*Rowsthat must be closed or the connection leaks. Batch operations in reasonable sizes (e.g., 1000 rows per batch) — row-by-row is too many round trips, millions at once holds locks and memory.
Pitfalls
- Using
db.Queryfor INSERT/UPDATE/DELETE — returns*Rowsthat must be closed, leaks connection if forgotten. Usedb.Exec. - Not deferring
rows.Close()— connection leaks back to pool slowly, eventually exhausting it. - Forgetting to handle
sql.ErrNoRows— it's a sentinel error, not a real failure. - Not setting
MaxIdleConns— defaults to 2, causing connection storms under load. - Using ORMs — they generate unpredictable SQL, make query optimization impossible, and break debugging.
- Opening and closing connections for each operation — use a shared pool initialized once at startup.
Verification
- No SQL string concatenation in the codebase — all queries use parameterized placeholders.
- Every
QueryContexthasdefer rows.Close()immediately following. - Connection pool configured at startup with explicit MaxOpenConns, MaxIdleConns, ConnMaxLifetime.
- No GORM or other ORM imports in production code.
sql.ErrNoRowsis handled explicitly in every query function.