gosqlite

package module
v0.0.0-...-e15201a Latest Latest
Warning

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

Go to latest
Published: Jul 19, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

README

gosqlite package

When I make some of my projects, I use a SQLite database. I realized I ended up writing very similar code for each project to connect to the database, execute queries, and handle results. To avoid this repetition, I created the gosqlite package. It utilizes go's reflect package to handle different types of structs to create tables, and execute a basic insert, select, update, delete, and list operations.

Usage

To use the gosqlite package, you need to import it in your Go project. Here's a basic example of how to use it:

import "github.com/z46-dev/gosqlite"

Now, let's create a struct that represents a table in the database. This package uses custom struct tags to define table structure, and its fields. Here's an example of a struct that represents a User table:

type User struct {
    Username string     `gosqlite:"username,primary,unique"`
    Password string     `gosqlite:"password"`
    Email    string     `gosqlite:"email,unique"`
    CreatedAt time.Time `gosqlite:"creation"`
}

Foreign keys are declared in the same tag with fkey:StructGoName.SQLiteFieldName. Add ondelete:cascade to opt into cascading child deletes:

type Team struct {
    ID int `gosqlite:"id,primary,increment"`
}

type Player struct {
    ID     int `gosqlite:"id,primary,increment"`
    TeamID int `gosqlite:"team_id,fkey:Team.id"`
}

type Match struct {
    ID     int `gosqlite:"id,primary,increment"`
    TeamID int `gosqlite:"team_id,fkey:Team.id,ondelete:cascade"`
}

Without ondelete:cascade, deleting a parent row while children still reference it will fail under SQLite foreign key enforcement.

Now, we should register this struct to set up the database tables:

func main() {
    var (
        handle *gosqlite.RegisteredStruct[User]
        err    error
    )

    if err = gosqlite.Begin(":memory:"); err != nil {
        panic(err)
    }

    if handle, err = gosqlite.Register(User{}); err != nil {
        panic(err)
    }
}

You can now call functions on the handle to perform database operations.

time.Time fields are stored as SQL DATETIME values, and time.Duration fields are stored as SQL INTEGER nanoseconds, so both can be filtered and ordered directly in SQL.

For larger tables, filters can also be used for efficient row counts and pruning the oldest rows without loading full records:

total, err := handle.Count()
if err != nil {
    panic(err)
}

deleted, err := handle.DeleteWithFilter(
    gosqlite.NewFilter().
        Ordering(handle.FieldByGoName("CreatedAt"), true).
        Limit(100),
)
if err != nil {
    panic(err)
}

_ = total
_ = deleted

Contributing

This was thrown together in a few hours, so there are likely many improvements that can be made. If you have suggestions or improvements, feel free to open an issue or submit a pull request. I would love to see this package grow.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrMigrationDestructive = errors.New("migration requires destructive changes")
	ErrMigrationNotNull     = errors.New("migration introduces NOT NULL column without data")
)
View Source
var (
	ErrDatabaseInitialized    = fmt.Errorf("database already initialized")
	ErrDatabaseNotInitialized = fmt.Errorf("database not initialized")
)

Functions

This section is empty.

Types

type BeginOption

type BeginOption func(*beginConfig)

func WithConnectionPool

func WithConnectionPool(maxOpenConns, maxIdleConns int) BeginOption

WithConnectionPool overrides Begin's default single-connection sql.DB pool. Keep the default when using SQLite :memory: DSNs unless you intentionally use a shared in-memory URI, because separate connections can observe different databases.

type Driver

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

func Begin

func Begin(dbPath string, opts ...BeginOption) (driver *Driver, err error)

func (*Driver) Close

func (d *Driver) Close() (err error)

func (*Driver) GetFilePath

func (d *Driver) GetFilePath() (path string)

type Filter

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

func NewFilter

func NewFilter() *Filter

func (*Filter) And

func (f *Filter) And() *Filter

func (*Filter) Build

func (f *Filter) Build() (sqlFragment string, args []any, err error)

func (*Filter) CloseGroup

func (f *Filter) CloseGroup() *Filter

func (*Filter) KeyCmp

func (f *Filter) KeyCmp(key *RegisteredStructField, op SQLOperator, value any) *Filter

func (*Filter) KeyHasAllBits

func (f *Filter) KeyHasAllBits(key *RegisteredStructField, mask any) *Filter

func (*Filter) KeyHasAnyBits

func (f *Filter) KeyHasAnyBits(key *RegisteredStructField, mask any) *Filter

func (*Filter) KeyHasNoBits

func (f *Filter) KeyHasNoBits(key *RegisteredStructField, mask any) *Filter

func (*Filter) Limit

func (f *Filter) Limit(n int) *Filter

func (*Filter) Offset

func (f *Filter) Offset(n int) *Filter

func (*Filter) OpenGroup

func (f *Filter) OpenGroup() *Filter

func (*Filter) Or

func (f *Filter) Or() *Filter

func (*Filter) Ordering

func (f *Filter) Ordering(field *RegisteredStructField, asc bool) *Filter

type ForeignKeyAction

type ForeignKeyAction string
const (
	ForeignKeyActionCascade ForeignKeyAction = "CASCADE"
)

type ForeignKeyRef

type ForeignKeyRef struct {
	TableName  string
	ColumnName string
	OnDelete   ForeignKeyAction
}

type MigrationOptions

type MigrationOptions struct {
	AllowDestructive bool
	Renames          map[string]string // old column name -> new column name
}

type MigrationReport

type MigrationReport struct {
	Table          string
	AddedColumns   []string
	DroppedColumns []string
	ChangedColumns []string
	RenamedColumns map[string]string // old column name -> new column name
	Rebuilt        bool
}

type RegisteredStruct

type RegisteredStruct[T any] struct {
	Name   string
	Type   reflect.Type
	Fields []RegisteredStructField

	PrimaryKeyField RegisteredStructField
	// contains filtered or unexported fields
}

func Register

func Register[T any](driver *Driver, structInstance T) (registered *RegisteredStruct[T], err error)

func (*RegisteredStruct[T]) Count

func (r *RegisteredStruct[T]) Count() (int64, error)

func (*RegisteredStruct[T]) CountWithFilter

func (r *RegisteredStruct[T]) CountWithFilter(filter *Filter) (int64, error)

func (*RegisteredStruct[T]) Delete

func (r *RegisteredStruct[T]) Delete(primaryKeyValue any) error

func (*RegisteredStruct[T]) DeleteWithFilter

func (r *RegisteredStruct[T]) DeleteWithFilter(filter *Filter) (int64, error)

func (*RegisteredStruct[T]) FieldByGoName

func (r *RegisteredStruct[T]) FieldByGoName(goName string) *RegisteredStructField

func (*RegisteredStruct[T]) FieldBySQLName

func (r *RegisteredStruct[T]) FieldBySQLName(sqlName string) *RegisteredStructField

func (*RegisteredStruct[T]) Insert

func (r *RegisteredStruct[T]) Insert(item *T) error

func (*RegisteredStruct[T]) List

func (r *RegisteredStruct[T]) List() ([]any, error)

func (*RegisteredStruct[T]) Migrate

func (r *RegisteredStruct[T]) Migrate(opts MigrationOptions) (*MigrationReport, error)

func (*RegisteredStruct[T]) Select

func (r *RegisteredStruct[T]) Select(primaryKeyValue any) (item *T, err error)

func (*RegisteredStruct[T]) SelectAll

func (r *RegisteredStruct[T]) SelectAll() ([]*T, error)

func (*RegisteredStruct[T]) SelectAllWithFilter

func (r *RegisteredStruct[T]) SelectAllWithFilter(filter *Filter) ([]*T, error)

func (*RegisteredStruct[T]) Update

func (r *RegisteredStruct[T]) Update(item *T) error

func (*RegisteredStruct[T]) UpdateWithFilter

func (r *RegisteredStruct[T]) UpdateWithFilter(filter *Filter, assignments ...UpdateAssignment) (int64, error)

func (*RegisteredStruct[T]) UpdateWithFilterReturning

func (r *RegisteredStruct[T]) UpdateWithFilterReturning(filter *Filter, returning []*RegisteredStructField, assignments ...UpdateAssignment) ([]ReturnedValues, error)

type RegisteredStructField

type RegisteredStructField struct {
	Opts         SQLTagOpts
	RealName     string
	Type         reflect.Type
	Index        []int
	InternalType TypeRepresentation
}

type ReturnedValues

type ReturnedValues map[string]any

type SQLOperator

type SQLOperator string
const (
	OpEqual              SQLOperator = "="
	OpNotEqual           SQLOperator = "!="
	OpGreaterThan        SQLOperator = ">"
	OpLessThan           SQLOperator = "<"
	OpGreaterThanOrEqual SQLOperator = ">="
	OpLessThanOrEqual    SQLOperator = "<="
	OpLike               SQLOperator = "LIKE"
	OpIn                 SQLOperator = "IN"
	OpNotIn              SQLOperator = "NOT IN"
	OpIsNull             SQLOperator = "IS NULL"
	OpIsNotNull          SQLOperator = "IS NOT NULL"
)

type SQLTagOpts

type SQLTagOpts struct {
	KeyName    string
	PrimaryKey bool
	AutoIncr   bool
	Unique     bool
	NotNull    bool
	ForeignKey *ForeignKeyRef
}

func (SQLTagOpts) HasForeignKey

func (o SQLTagOpts) HasForeignKey() bool

type TypeRepresentation

type TypeRepresentation uint8
const (
	TypeRepInt TypeRepresentation = iota
	TypeRepUint
	TypeRepString
	TypeRepBool
	TypeRepArrayBlob
	TypeRepStructBlob
	TypeRepFloat
	TypeRepMapBlob
	TypeRepPointer
	TypeRepTime
)

type UpdateAssignment

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

func SetAdd

func SetAdd(field *RegisteredStructField, value any) UpdateAssignment

func SetDiv

func SetDiv(field *RegisteredStructField, value any) UpdateAssignment

func SetExpr

func SetExpr(field *RegisteredStructField, expr string, args ...any) UpdateAssignment

func SetField

func SetField(field *RegisteredStructField, value any) UpdateAssignment

func SetMul

func SetMul(field *RegisteredStructField, value any) UpdateAssignment

func SetSub

func SetSub(field *RegisteredStructField, value any) UpdateAssignment

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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