sql

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 17, 2026 License: MIT Imports: 15 Imported by: 3

README

sql — SQL database abstraction with leader/follower routing

import "github.com/downsized-devs/sdk-go/sql"

Stability: Stable — see STABILITY.md

Multi-driver (MySQL, Postgres, SQLite) database client built on sqlx, with leader/follower split, prepared statements, transactions, and built-in Prometheus instrumentation.

Features

  • Leader/follower routing (Leader(ctx), Follower(ctx))
  • Multi-driver: github.com/go-sql-driver/mysql, github.com/lib/pq, modernc.org/sqlite
  • Transactions with BeginTx
  • Prepared statements (Prepare)
  • Automatic instrument metrics for query timings + connection pool stats
  • ErrNotFound sentinel for "no rows" lookups

Installation

go get github.com/downsized-devs/sdk-go/sql

Quick Start

import (
    "context"

    "github.com/downsized-devs/sdk-go/instrument"
    "github.com/downsized-devs/sdk-go/logger"
    "github.com/downsized-devs/sdk-go/sql"
)

log := logger.Init(logger.Config{Level: "info"})
metrics := instrument.Init(instrument.Config{Enabled: true})

db := sql.Init(sql.Config{
    Driver: "postgres",
    Leader: sql.ConnConfig{
        DSN: "postgres://user:pass@leader:5432/app?sslmode=disable",
    },
    Follower: sql.ConnConfig{
        DSN: "postgres://user:pass@follower:5432/app?sslmode=disable",
    },
}, log, metrics)
defer db.Stop()

var u User
if err := db.Follower(ctx).Get(ctx, &u, "SELECT * FROM users WHERE id=$1", 1); err != nil {
    if errors.Is(err, sql.ErrNotFound) { /* miss */ }
}

API Reference

Symbol Signature
Init func Init(cfg Config, log logger.Interface, metrics instrument.Interface) Interface
Interface.Leader (ctx) Command — read/write connection.
Interface.Follower (ctx) Command — read-only connection.
Interface.Stop () error — close all pools.
Command.Query (ctx, dest, q, args...) error
Command.Exec (ctx, q, args...) (Result, error)
Command.Get (ctx, dest, q, args...) error — single row.
Command.BeginTx (ctx, TxOptions) (Tx, error)
Command.Prepare (ctx, q) (Stmt, error)

ErrNotFound is returned by Get when the row is missing.

Configuration

Field Required Description
Driver yes mysql, postgres, or sqlite.
Leader.DSN / Follower.DSN yes Connection strings.
Leader.MaxOpen, MaxIdle, MaxLifetime no Pool tuning. Same for follower.

Examples

Run a transaction
tx, err := db.Leader(ctx).BeginTx(ctx, sql.TxOptions{})
if err != nil { return err }
defer tx.Rollback()

if _, err := tx.Exec(ctx, "UPDATE accounts SET balance = balance - $1 WHERE id = $2", 100, 1); err != nil {
    return err
}
return tx.Commit()
Use a prepared statement
stmt, _ := db.Leader(ctx).Prepare(ctx, "INSERT INTO events(name, payload) VALUES (?, ?)")
defer stmt.Close()
for _, e := range events {
    _, _ = stmt.Exec(ctx, e.Name, e.Payload)
}

Error Handling

Error Cause Action
sql.ErrNotFound Get returned no rows. Treat as miss.
Coded errors Connection, syntax, constraint. Inspect with errors.GetCode(err).

Dependencies

  • Internal: codes, errors, instrument, logger
  • External: github.com/jmoiron/sqlx, github.com/go-sql-driver/mysql, github.com/lib/pq, modernc.org/sqlite

Testing

go test ./sql/...

Five test files exercise sqlite in-memory; MySQL/Postgres tests require live databases.

Contributing

See CONTRIBUTING.md. Any change to the Interface or Command interface is breaking — coordinate with downstream services.

  • query — dynamic WHERE/ORDER builder that consumes sql.Interface.
  • null — nullable types matching SQL semantics.
  • instrument — receives DB pool stats and query timings.
  • redis — pair for cache-aside reads.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrNotFound = sql.ErrNoRows

Functions

This section is empty.

Types

type Command

type Command interface {
	Close() error
	Ping(ctx context.Context) error
	In(query string, args ...interface{}) (string, []interface{}, error)
	Rebind(query string) string
	QueryIn(ctx context.Context, name string, query string, args ...interface{}) (*sqlx.Rows, error)
	QueryRow(ctx context.Context, name string, query string, args ...interface{}) (*sqlx.Row, error)
	Query(ctx context.Context, name string, query string, args ...interface{}) (*sqlx.Rows, error)
	NamedQuery(ctx context.Context, name string, query string, arg interface{}) (*sqlx.Rows, error)
	Prepare(ctx context.Context, name string, query string) (CommandStmt, error)

	NamedExec(ctx context.Context, name string, query string, args interface{}) (sql.Result, error)
	Exec(ctx context.Context, name string, query string, args ...interface{}) (sql.Result, error)
	BeginTx(ctx context.Context, name string, opts TxOptions) (CommandTx, error)

	Get(ctx context.Context, name string, query string, dest interface{}, args ...interface{}) error
}

type CommandStmt

type CommandStmt interface {
	Close() error
	Select(name string, dest interface{}, args ...interface{}) error
	Get(name string, dest interface{}, args ...interface{}) error
	QueryRow(name string, args ...interface{}) (*sqlx.Row, error)
	Query(name string, args ...interface{}) (*sqlx.Rows, error)
	Exec(name string, args ...interface{}) (sql.Result, error)
}

type CommandTx

type CommandTx interface {
	Commit() error
	Rollback()
	Rebind(query string) string
	Select(name string, query string, dest interface{}, args ...interface{}) error
	Get(name string, query string, dest interface{}, args ...interface{}) error
	QueryRow(name string, query string, args ...interface{}) (*sqlx.Row, error)
	Query(name string, query string, args ...interface{}) (*sqlx.Rows, error)
	Prepare(name string, query string) (CommandStmt, error)

	NamedExec(name string, query string, args interface{}) (sql.Result, error)
	Exec(name string, query string, args ...interface{}) (sql.Result, error)
	Stmt(name string, stmt *sqlx.Stmt) CommandStmt
}

type Config

type Config struct {
	UseInstrument bool
	LogQuery      bool
	Driver        string
	Name          string
	Leader        ConnConfig
	Follower      ConnConfig
}

type ConnConfig

type ConnConfig struct {
	Host     string
	Port     int
	DB       string
	User     string
	Password string
	SSL      bool
	Schema   string
	Options  ConnOptions
	MockDB   *sql.DB
}

type ConnOptions

type ConnOptions struct {
	MaxLifeTime time.Duration
	MaxIdle     int
	MaxOpen     int
}

type Interface

type Interface interface {
	Leader() Command
	Follower() Command
	Stop()
}

func Init

func Init(cfg Config, log logger.Interface, instr instrument.Interface) Interface

type TxOptions

type TxOptions struct {
	Isolation sql.IsolationLevel
	ReadOnly  bool
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL