sqlutil

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2026 License: MPL-2.0 Imports: 6 Imported by: 0

README

sqliteutil

sqliteutil provides utility functions to work with the standard database/sql package in Go. Its main components are:

  1. Meta[T] that contains information about the column names and struct fields obtained by reflection.
  2. Builder a builder to generate parameterized SQL with printf syntax.

Struct Tags

The Meta[T] object works using struct tags. Each field that should be retrievable must contain a struct tag named db.

The tag value consists of a field name, and optional "tags". The empty tag is implicitly given to all struct fields.

Example


type Car struct {
	ID    int64  `db:"id,select"`
	Make  string `db:"make,select,insert,update"`
	Model string `db:"model,select,insert,update"`
	Year  int64  `db:"year,select,insert,update"`
	Color string `db:"color,select,insert,update"`
}

// extract all columns with the "insert" tag
var insertCarMeta = sqlutil.NewMeta[Car]("insert")

func InsertCarSimple(tx *sql.Tx, c Car) (int64, error) {
	b := sqlutil.Builder{Dialect: sqlutil.SQLite}
	insertCarMeta.InsertValues(&b, "car", c)

	id, err := b.ExecLastInsertID(tx)
	return id, err
}

func InsertCar(tx *sql.Tx, c Car) (int64, error) {
	// create a Builder for sqlite (using the ? placeholder)
	// and write the query. It will end up looking like
	// INSERT INTO car (make, model, year, color) VALUES (?, ?, ?, ?)
	// and the arguments will be pointers to the correspoinging struct fields
	b := sqlutil.Builder{Dialect: sqlutil.SQLite}
	b.Printf("INSERT INTO car (%s) VALUES (%s)", insertCarMeta.Cols(), insertCarMeta.Vals(c))

	// execute the query using the (matching) number of arguments
	// to simplify this, there is a helper b.Exec(tx)
	res, err := tx.Exec(b.String(), b.Args()...)
	if err != nil {
		return 0, fmt.Errorf("exec: %w", err)
	}
	id, err := res.LastInsertId()
	if err != nil {
		return 0, fmt.Errorf("get last insert id: %w", err)
	}
	return id, nil
}

// extract all columns with the "select" tag
var selectCarMeta = sqlutil.NewMeta[Car]("select")

func SelectCars(tx *sql.Tx) ([]Car, error) {
	var c Car
	// build the query only using the "select" cols
	// the Fields method can be used in combination with
	// b.ScanFields and b.ScanRowFields.
	b := sqlutil.Builder{Dialect: sqlutil.SQLite}
	b.Printf("SELECT %v FROM car", selectCarMeta.Fields(&c))

	// execute the query
	// See sqlutil.Slice and sqlutil.MappedSlice for other scan targets
	// that support straight forward scanning into slices.
	var cars []Car
	rows := b.ScanFields(tx)
	defer rows.Close()
	for rows.Next() {
		cars = append(cars, c)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("scan fields: %w", err)
	}

	return cars, nil
}

// extract all columns with the "update" tag
var updateCarMeta = sqlutil.NewMeta[Car]("update")

func UpdateCarSimple(tx *sql.Tx, c Car) error {
	// this generates the same SQL as UpdateCar
	b := sqlutil.Builder{Dialect: sqlutil.SQLite}
	updateCarMeta.Update(&b, "car", c)
	_, err := b.Exec(tx)
	if err != nil {
		return fmt.Errorf("exec: %w", err)
	}
	return nil
}

func UpdateCar(tx *sql.Tx, c Car) error {
	// create a Builder for sqlite (using the ? placeholder)
	// and use it to format the update statement.
	// JoinBy is used to join multiple calls to Printf
	// with ", "
	b := sqlutil.Builder{Dialect: sqlutil.SQLite}
	b.Printf("UPDATE car SET ")
	b.JoinBy(", ")
	for col, val := range updateCarMeta.IterVals(c) {
		b.Printf("%s = %v", col, val)
	}
	b.JoinBy("")
	b.Printf(" WHERE id = %v", c.ID)
	_, err := b.Exec(tx)
	if err != nil {
		return fmt.Errorf("exec: %w", err)
	}
	return nil
}

Licensing

sqlutil is released under the MPL v2.0 license

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ScanCols any = new(scanColsImpl)

Functions

func Scan added in v0.2.0

func Scan(s SQLRow, d ...any) error

func ScriptMigration

func ScriptMigration(script string) func(*sql.Tx) error

Types

type Builder

type Builder struct {
	Dialect Dialect
	// contains filtered or unexported fields
}

func (*Builder) Args

func (b *Builder) Args() []any

func (*Builder) Exec added in v0.0.4

func (b *Builder) Exec(db DB) (sql.Result, error)

func (*Builder) ExecLastInsertID added in v0.2.0

func (b *Builder) ExecLastInsertID(db DB) (int64, error)

func (*Builder) ExecRowsAffected added in v0.2.0

func (b *Builder) ExecRowsAffected(db DB) (int64, error)

func (*Builder) JoinBy added in v0.2.0

func (b *Builder) JoinBy(sep string)

func (*Builder) Printf

func (b *Builder) Printf(format string, a ...any)

func (*Builder) Query added in v0.0.4

func (b *Builder) Query(db DB) (*sql.Rows, error)

func (*Builder) QueryRow added in v0.0.4

func (b *Builder) QueryRow(db DB) *sql.Row

func (*Builder) Reset added in v0.0.9

func (b *Builder) Reset()

func (*Builder) Scan added in v0.2.0

func (b *Builder) Scan(db DB, d ...any) Rows

func (*Builder) ScanArgs added in v0.2.0

func (b *Builder) ScanArgs() []any

func (*Builder) ScanFields added in v0.2.0

func (b *Builder) ScanFields(db DB) Rows

func (*Builder) ScanRow added in v0.0.4

func (b *Builder) ScanRow(db DB, d ...any) error

func (*Builder) ScanRowFields added in v0.2.0

func (b *Builder) ScanRowFields(db DB) error

func (*Builder) String

func (b *Builder) String() string

type Cols

type Cols []Raw

Cols represent the columns of a struct

func (Cols) String added in v0.2.0

func (c Cols) String() string

func (Cols) WriteSQL added in v0.2.0

func (c Cols) WriteSQL(w *RawWriter)

WriteSQL implements SQLWriter for Cols

type DB added in v0.0.9

type DB interface {
	Exec(query string, a ...any) (sql.Result, error)
	Query(query string, a ...any) (*sql.Rows, error)
	QueryRow(query string, a ...any) *sql.Row
}

DB describes the common interface of *sql.Tx and *sql.Rows

type Dialect added in v0.0.9

type Dialect interface {
	WriteParam(sb *strings.Builder, idx int)
}
var (
	SQLite Dialect = sqliteDialectImpl{}
)

type Fields added in v0.2.0

type Fields struct {
	// contains filtered or unexported fields
}

type Meta added in v0.2.0

type Meta[T any] struct {
	// contains filtered or unexported fields
}

Meta contains field information about a struct

func NewMeta added in v0.2.0

func NewMeta[T any](tag string) Meta[T]

func (*Meta[T]) AssignFields added in v0.2.0

func (m *Meta[T]) AssignFields(b *Builder, v T)

AssignFields prints col1 = ?, col2 = ? ...

func (*Meta[T]) Cols added in v0.2.0

func (m *Meta[T]) Cols() Cols

func (*Meta[T]) ColsPrefix added in v0.2.0

func (m *Meta[T]) ColsPrefix(prefix string) Cols

func (*Meta[T]) Fields added in v0.2.0

func (m *Meta[T]) Fields(v *T) Fields

func (*Meta[T]) FieldsPrefix added in v0.2.0

func (m *Meta[T]) FieldsPrefix(prefix string, v *T) Fields

func (*Meta[T]) InsertValues added in v0.2.0

func (m *Meta[T]) InsertValues(b *Builder, table string, vals ...T)

InsertValues prints INSERT INTO ... VALUES (...)...

func (*Meta[T]) IterPtrs added in v0.2.0

func (m *Meta[T]) IterPtrs(v *T) iter.Seq2[Raw, any]

func (*Meta[T]) IterVals added in v0.2.0

func (m *Meta[T]) IterVals(v T) iter.Seq2[Raw, any]

func (*Meta[T]) Ptrs added in v0.2.0

func (m *Meta[T]) Ptrs(v *T) Vals

func (*Meta[T]) SelectFields added in v0.2.0

func (m *Meta[T]) SelectFields(b *Builder, table string, v *T)

SelectFields prints SELECT ... FROM ...

func (*Meta[T]) Update added in v0.2.0

func (m *Meta[T]) Update(b *Builder, table string, v T)

func (*Meta[T]) Vals added in v0.2.0

func (m *Meta[T]) Vals(v T) Vals

type Migration

type Migration struct {
	Version int64
	Migrate func(tx *sql.Tx) error
}

type Raw

type Raw string

Raw will be formatted as raw SQL

type RawWriter added in v0.2.0

type RawWriter struct {
	// contains filtered or unexported fields
}

func (*RawWriter) Write added in v0.2.0

func (b *RawWriter) Write(d []byte) (int, error)

Write appends the contents of p to b's buffer. Write always returns len(p), nil.

func (*RawWriter) WriteParam added in v0.2.0

func (b *RawWriter) WriteParam(v any)

type Rows added in v0.2.0

type Rows struct {
	// contains filtered or unexported fields
}

func (*Rows) Close added in v0.2.0

func (r *Rows) Close() error

func (*Rows) Err added in v0.2.0

func (r *Rows) Err() error

func (*Rows) Next added in v0.2.0

func (r *Rows) Next() bool

Next continues with the next row and scans into the arguments provided to ScanRow

type SQLRow added in v0.2.0

type SQLRow interface {
	Scan(d ...any) error
}

SQLRow describes the common interface of *sql.Rows and *sql.Row

type SQLWriter added in v0.2.0

type SQLWriter interface {
	WriteSQL(w *RawWriter)
}

type Vals

type Vals []any

Vals represent the values or pointers to / of struct fields

func (Vals) WriteSQL added in v0.2.0

func (v Vals) WriteSQL(w *RawWriter)

WriteSQL implements SQLWriter for Vals

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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