flsql

package
v0.296.0 Latest Latest
Warning

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

Go to latest
Published: Apr 9, 2025 License: Apache-2.0 Imports: 19 Imported by: 3

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ContextSQLTxOptions contextkit.ValueHandler[ctxKeyTxOptions, *sql.TxOptions]

Functions

func JoinColumnName

func JoinColumnName(cns []ColumnName, format string, sep string) string
Example
package main

import (
	"go.llib.dev/frameless/pkg/flsql"
)

func main() {
	_ = flsql.JoinColumnName([]flsql.ColumnName{"foo", "bar", "baz"}, "%q", ", ")
	// `"foo", "bar", "baz"`
}
Output:

func MakeErrSQLRow

func MakeErrSQLRow(err error) sql.Row

func MakeRowsIterator added in v0.285.0

func MakeRowsIterator[T any](rows Rows, mapper RowMapper[T]) iterkit.ErrSeq[T]

MakeRowsIterator allow you to use the same iterator pattern with sql.Rows structure. it allows you to do dynamic filtering, pipeline/middleware pattern on your sql results by using this wrapping around it. it also makes testing easier with the same Interface interface.

func QueryMany added in v0.286.0

func QueryMany[T any](c Queryable, ctx context.Context, mapper RowMapper[T], query string, args ...any) (iterkit.ErrSeq[T], error)

Types

type ColumnName

type ColumnName string

func SplitArgs

func SplitArgs(cargs map[ColumnName]any) ([]ColumnName, []any)

type Connection

type Connection interface {
	comproto.OnePhaseCommitProtocol
	Queryable
	io.Closer
}

Connection represent an open connection. Connection will respect the transaction state in the received context.Context.

type ConnectionAdapter

type ConnectionAdapter[DB, TX any] struct {
	// DB is the underlying Database type to access.
	// It is ideal if ConnectionAdapter used as the Connection type implementation, but you need access to not exposed functionalities.
	//
	// 		type Connection flsql.ConnectionAdapter[*sql.DB, *sql.Tx]
	//
	DB *DB
	// TxAdapter provides the mapping for a native driver specific TX type to be usable as a Queryable.
	TxAdapter func(tx *TX) Queryable
	// DBAdapter provides the mapping for a native driver specific DB type to be usable as a Queryable.
	DBAdapter func(db *DB) Queryable
	// Begin is a function that must create a new transaction that is also a connection.
	Begin func(ctx context.Context, db *DB) (*TX, error)
	// Commit is a function that must commit a given transaction.
	Commit func(ctx context.Context, tx *TX) error
	// Rollback is a function that must rollback a given transaction.
	Rollback func(ctx context.Context, tx *TX) error

	// OnClose [optional] is used to implement the io.Closer.
	// If The ConnectionAdapter needs to close something,
	// then this function can be used for that.
	//
	// default: DB.Close()
	OnClose func() error
	// ErrTxDone is the error returned when the transaction is already finished.
	// ErrTxDone is an optional field.
	//
	// default: sql.ErrTxDone
	ErrTxDone error
}

ConnectionAdapter is generic implementation to handle query interactions which are aware of trasnactions within the context.

Example:

type Connection = flsql.ConnectionAdapter[*sql.DB, *sql.Tx]
Example
package main

import (
	"context"
	"database/sql"
	"os"

	"go.llib.dev/frameless/pkg/flsql"
)

func main() {
	db, err := sql.Open("dbname", os.Getenv("DATABASE_URL"))
	if err != nil {
		panic(err)
	}

	_ = flsql.ConnectionAdapter[sql.DB, sql.Tx]{
		DB: db,

		DBAdapter: flsql.QueryableSQL[*sql.DB],
		TxAdapter: flsql.QueryableSQL[*sql.Tx],

		Begin: func(ctx context.Context, db *sql.DB) (*sql.Tx, error) {
			// TODO: integrate begin tx options
			return db.BeginTx(ctx, nil)
		},

		Commit: func(ctx context.Context, tx *sql.Tx) error {
			return tx.Commit()
		},

		Rollback: func(ctx context.Context, tx *sql.Tx) error {
			return tx.Rollback()
		},
	}
}
Output:

func SQLConnectionAdapter

func SQLConnectionAdapter(db *sql.DB) ConnectionAdapter[sql.DB, sql.Tx]

SQLConnectionAdapter is a built-in ConnectionAdapter usage for the stdlib sql.DB/sql.Tx. This can be used with any sql driver that integartes with the sql stdlib.

Example
package main

import (
	"database/sql"
	"os"

	"go.llib.dev/frameless/pkg/flsql"
)

func main() {
	db, err := sql.Open("dbname", os.Getenv("DATABASE_URL"))
	if err != nil {
		panic(err)
	}

	_ = flsql.SQLConnectionAdapter(db)
}
Output:

func (ConnectionAdapter[DB, TX]) BeginTx

func (c ConnectionAdapter[DB, TX]) BeginTx(ctx context.Context) (context.Context, error)

func (ConnectionAdapter[DB, TX]) Close

func (c ConnectionAdapter[DB, TX]) Close() error

func (ConnectionAdapter[DB, TX]) CommitTx

func (c ConnectionAdapter[DB, TX]) CommitTx(ctx context.Context) error

func (ConnectionAdapter[DB, TX]) ExecContext

func (c ConnectionAdapter[DB, TX]) ExecContext(ctx context.Context, query string, args ...any) (Result, error)

func (ConnectionAdapter[DB, TX]) LookupTx added in v0.248.0

func (c ConnectionAdapter[DB, TX]) LookupTx(ctx context.Context) (*TX, bool)

func (ConnectionAdapter[DB, TX]) QueryContext

func (c ConnectionAdapter[DB, TX]) QueryContext(ctx context.Context, query string, args ...any) (Rows, error)

func (ConnectionAdapter[DB, TX]) QueryRowContext

func (c ConnectionAdapter[DB, TX]) QueryRowContext(ctx context.Context, query string, args ...any) Row

func (ConnectionAdapter[DB, TX]) RollbackTx

func (c ConnectionAdapter[DB, TX]) RollbackTx(ctx context.Context) error

type DTO added in v0.236.0

type DTO interface {
	driver.Valuer
	sql.Scanner
}

DTO (Data Transfer Object) is an object used to transfer data between the database and your application. It acts as a bridge between the entity field types in your application and the table column types in your database, to making it easier to map data between them. This helps keep the data structure consistent when passing information between layers or systems.

func Timestamp added in v0.236.0

func Timestamp(pointer *time.Time, layout string, tz *time.Location) DTO

type DTOJSON added in v0.244.0

type DTOJSON[T any] struct {
	Pointer *T

	DisallowUnknownFields bool
}

func JSON added in v0.236.0

func JSON[T any](pointer *T) *DTOJSON[T]

func (*DTOJSON[T]) MarshalJSON added in v0.244.0

func (m *DTOJSON[T]) MarshalJSON() ([]byte, error)

func (*DTOJSON[T]) Scan added in v0.244.0

func (m *DTOJSON[T]) Scan(value any) error

func (*DTOJSON[T]) UnmarshalJSON added in v0.244.0

func (m *DTOJSON[T]) UnmarshalJSON(data []byte) error

func (DTOJSON[T]) Value added in v0.244.0

func (m DTOJSON[T]) Value() (driver.Value, error)

type MapScan

type MapScan[ENT any] func(v *ENT, s Scanner) error

func (MapScan[ENT]) Map

func (ms MapScan[ENT]) Map(scanner Scanner) (ENT, error)

type Mapping

type Mapping[ENT, ID any] struct {
	// TableName is the name of the table in the database.
	TableName string
	// ToQuery suppose to return back with the column names that needs to be selected from the table,
	// and the corresponding scan function that
	// ctx enables you to accept custom query instructions through the context if you require that.
	ToQuery func(ctx context.Context) ([]ColumnName, MapScan[ENT])
	// QueryID will convert an ID into query components—specifically,
	// column names and their corresponding values—that represent the ID in an SQL WHERE statement.
	// If ID is nil, then
	QueryID func(id ID) (QueryArgs, error)
	// ToArgs converts an entity pointer into a list of query arguments for CREATE or UPDATE operations.
	// It must handle empty or zero values and still return a valid column statement.
	ToArgs func(ENT) (QueryArgs, error)
	// Prepare is an optional field that allows you to configure an entity prior to crud.Create call.
	// This is a good place to add support in your Repository implementation for custom ID injection or special timestamp value arrangement.
	//
	// To have this working, the user of Mapping needs to call Mapping.OnPrepare method within in its crud.Create method implementation.
	Prepare func(context.Context, *ENT) error
	// ID [optional] is a function that allows the ID lookup from an entity.
	// The returned ID value will be used to Lookup the ID value, or to set a new ID value.
	// Mapping will panic if ID func is provided, but returns a nil, as it is considered as implementation error.
	//
	// Example implementation:
	//
	// 		flsql.Mapping[Foo, FooID]{..., ID: func(v Foo) *FooID { return &v.ID }}
	//
	// default: extid.Lookup, extid.Set, which will use either the `ext:"id"` tag, or the `ENT.ID()` & `ENT.SetID()` methods.
	ID extid.Accessor[ENT, ID]
}

Mapping is a table mapping

Example
package main

import (
	"context"

	"go.llib.dev/frameless/pkg/flsql"
)

func main() {
	type ExampleEntity struct {
		ID   int64
		Col1 int
		Col2 string
		Col3 bool
	}
	_ = flsql.Mapping[ExampleEntity, int64]{
		TableName: `"public"."entities"`,

		QueryID: func(id int64) (flsql.QueryArgs, error) {
			return flsql.QueryArgs{"entity_id": id}, nil
		},

		ToArgs: func(ee ExampleEntity) (flsql.QueryArgs, error) {
			return flsql.QueryArgs{
				"entity_id": ee.ID,
				"col1":      ee.Col1,
				"col2":      ee.Col2,
				"col3":      ee.Col3,
			}, nil
		},

		ToQuery: func(ctx context.Context) ([]flsql.ColumnName, flsql.MapScan[ExampleEntity]) {
			return []flsql.ColumnName{"entity_id", "col1", "col2", "col3"},
				func(e *ExampleEntity, s flsql.Scanner) error {
					return s.Scan(&e.ID, &e.Col1, &e.Col2, &e.Col3)
				}
		},
	}
}
Output:

func (Mapping[ENT, ID]) OnPrepare added in v0.244.3

func (m Mapping[ENT, ID]) OnPrepare(ctx context.Context, ptr *ENT) error

type MigrationStep added in v0.238.0

type MigrationStep[C Queryable] struct {
	Up      func(C, context.Context) error
	UpQuery string

	Down      func(C, context.Context) error
	DownQuery string
}

func (MigrationStep[C]) MigrateDown added in v0.238.0

func (m MigrationStep[C]) MigrateDown(c C, ctx context.Context) error

func (MigrationStep[C]) MigrateUp added in v0.238.0

func (m MigrationStep[C]) MigrateUp(c C, ctx context.Context) error

type QueryArgs added in v0.238.0

type QueryArgs map[ColumnName]any

type Queryable

type Queryable interface {
	ExecContext(ctx context.Context, query string, args ...any) (Result, error)
	QueryContext(ctx context.Context, query string, args ...any) (Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...any) Row
}

func QueryableSQL

func QueryableSQL[SQLQ sqlQueryable](q SQLQ) Queryable

type QueryableAdapter

type QueryableAdapter struct {
	ExecFunc     func(ctx context.Context, query string, args ...any) (Result, error)
	QueryFunc    func(ctx context.Context, query string, args ...any) (Rows, error)
	QueryRowFunc func(ctx context.Context, query string, args ...any) Row
}

func (QueryableAdapter) ExecContext

func (a QueryableAdapter) ExecContext(ctx context.Context, query string, args ...any) (Result, error)

func (QueryableAdapter) QueryContext

func (a QueryableAdapter) QueryContext(ctx context.Context, query string, args ...any) (Rows, error)

func (QueryableAdapter) QueryRowContext

func (a QueryableAdapter) QueryRowContext(ctx context.Context, query string, args ...any) Row

type Result

type Result interface {
	// RowsAffected returns the number of rows affected by an
	// update, insert, or delete. Not every database or database
	// driver may support this.
	RowsAffected() (int64, error)
}

type Row

type Row interface {
	// Scan works the same as Rows. with the following exceptions. If no
	// rows were found it returns errNoRows. If multiple rows are returned it
	// ignores all but the first.
	Scan(dest ...any) error
}

type RowMapper added in v0.285.0

type RowMapper[T any] func(Scanner) (T, error)

func (RowMapper[T]) Map added in v0.285.0

func (fn RowMapper[T]) Map(s Scanner) (T, error)

type Rows

type Rows interface {
	// Closer is the interface that wraps the basic Close method.
	io.Closer
	// Err returns any error that occurred while reading.
	Err() error
	// Next prepares the next row for reading. It returns true if there is another
	// row and false if no more rows are available. It automatically closes rows
	// when all rows are read.
	Next() bool
	// Scan reads the values from the current row into dest values positionally.
	// dest can include pointers to core types, values implementing the Scanner
	// interface, and nil. nil will skip the value entirely. It is an error to
	// call Scan without first calling Next() and checking that it returned true.
	Scan(dest ...any) error
}

type Scanner

type Scanner interface{ Scan(dest ...any) error }

Jump to

Keyboard shortcuts

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