Documentation ¶
Overview ¶
Package sql defines a data map type with support for type-safe structured queries.
Designing a Schema ¶
Domain objects should typically be split into comparable index and value pairs.
// Domain type. type Customer struct { ID int64 Age uint Name string } // Internal Database Index Type. type dbID int64 // Internal Database Value Type. type dbCustomer struct { Name string Age uint } // Database representation. var customers sql.Map[dbID, dbCustomer]
Insert a record into the table, overwriting it if the ID already exists.
err := customers.Insert(ctx, id, &dbCustomer{Name: "Bob", Age: 23})
Lookup a record from the table.
customer, exists, err := customers.Lookup(ctx, id)
Delete the record from the table.
err := customers.Delete(ctx, id)
Search ¶
It is possible to search for records in the table and iterate through the results. For example, to search for customers with the age "22":
query := func(index *dbID, value *dbCustomer) sql.Query { return sql.Query{ sql.Index(&value.Age).Equals(22), } } for result := range customers.SearchFunc(ctx, query) { id, customer, err := result() if err != nil { return xray.Error(err) } fmt.Println(customer.Name) }
Query Builder ¶
Queries can be constructed using a series of operations:
sql.Index(&field).Equals(x) // field == x sql.Where(&field).AtLeast(x) // field >= x sql.Where(&field).AtMost(x) // field <= x sql.Where(&field).MoreThan(x) // field > x sql.Where(&field).LessThan(x) // field < x sql.Match(&field).Contains(x) // strings.Contains(field, x) sql.Match(&field).HasPrefix(x) // strings.HasPrefix(field, x) sql.Match(&field).HasSuffix(x) // strings.HasSuffix(field, x) sql.Order(&field).Increasing() // ORDER BY field ASC sql.Order(&field).Decreasing() // ORDER BY field DESC sql.Slice(from, upto) // data[from:upto] sql.Empty(&field) // reflect.ValueOf(field).IsZero() sql.Avoid(q) // !q // switch sql.Cases( sql.Index(&field).Equals(x), sql.Index(&field).Equals(y), ... )
Conditional Writes ¶
Each write operation supports conditionals, so that they can only be completed when the provided checks match the existing record (which may be a zero value).
ifBob := func(index *dbUUID, value *dbCustomer) sql.Query { return sql.Query{ sql.Index(&index).Equals(1), sql.Where(&value.Name).Equals("Bob",), } } ok, err := customers.UpdateFunc(ctx, ifBob, func(value dbCustomer) sql.Patch { return sql.Patch{ sql.Set(&value.Name, "Alice"), } }))
In the example above, the Customer with ID 1 will only have their name updated to "Alice" if their name was "Bob". If the customer's names is not Bob, ok will be false.
Statistics ¶
Statistics can be reported on a map in the database with sql.Counter.
var ( count sql.Counter[uint] total sql.Counter[uint] ) stats := func(index dbUUID, value dbCustomer) sql.Stats { return sql.Stats{ count.Add(), total.Sum(&value.Age), } } err := customers.OutputFunc(ctx, stats, func(index *dbUUID, value *dbCustomer) sql.Query { return sql.Query{ sql.Where(&value.Name).Equals("Bob"), } })
Index ¶
- Constants
- func Avoid(expr sodium.Expression) sodium.Expression
- func Cases(exprs ...sodium.Expression) sodium.Expression
- func Empty[V any](ptr *V) sodium.Expression
- func Index[V comparable](ptr *V) struct{ ... }
- func Match[V ~string](ptr *V) struct{ ... }
- func Merge(exprs ...sodium.Expression) sodium.Expression
- func New() sodium.Database
- func NewOutput(calcs []sodium.Calculation, scanner func(...any) error) ([]sodium.Value, error)
- func NewResult(table sodium.Table, scanner func(...any) error) ([]sodium.Value, error)
- func Order[V orderable](ptr *V) struct{ ... }
- func Set[V any](ptr *V, val V) sodium.Modification
- func Slice(from, upto int) sodium.Expression
- func Test(ctx context.Context, db Database) error
- func ValuesOf(val any) []sodium.Value
- func Where[V whereable](ptr *V) struct{ ... }
- type BatchFunc
- type Chan
- type Check
- type CheckFunc
- type Counter
- type Database
- type Flag
- type IncompatibleTypeError
- type Map
- func (m Map[K, V]) Delete(ctx context.Context, key K, check CheckFunc[V]) (bool, error)
- func (m Map[K, V]) Insert(ctx context.Context, key K, flag Flag, value V) error
- func (m Map[K, V]) Lookup(ctx context.Context, key K) (V, bool, error)
- func (m Map[K, V]) Mutate(ctx context.Context, key K, check CheckFunc[V], patch PatchFunc[V]) (bool, error)
- func (m Map[K, V]) Output(ctx context.Context, query QueryFunc[K, V], stats StatsFunc[K, V]) error
- func (m Map[K, V]) Search(ctx context.Context, query QueryFunc[K, V]) Chan[K, V]
- func (m Map[K, V]) UnsafeDelete(ctx context.Context, query QueryFunc[K, V]) (int, error)
- func (m Map[K, V]) Update(ctx context.Context, query QueryFunc[K, V], patch PatchFunc[V]) (int, error)
- type Patch
- type PatchFunc
- type Query
- type QueryFunc
- type Stats
- type StatsFunc
- type Table
- type UnsupportedTypeError
Constants ¶
const ( ErrDuplicate = errorString("record already exists") ErrTransactionUsage = errorString("empty transaction level") )
Variables ¶
This section is empty.
Functions ¶
func Avoid ¶
func Avoid(expr sodium.Expression) sodium.Expression
Avoid returns a new [Expression] that can be used inside a QueryFunc to filter for results that do not match the given expression.
func Cases ¶
func Cases(exprs ...sodium.Expression) sodium.Expression
Cases returns a new [Expression] that can be used inside a QueryFunc to filter for results that match any of the given expressions.
func Empty ¶
func Empty[V any](ptr *V) sodium.Expression
Empty returns a new [Expression] for the given ptr that can be used inside a QueryFunc to filter for results that have an empty value at the given column.
func Index ¶
func Index[V comparable](ptr *V) struct { Equals func(V) sodium.Expression // matches values that are equal to the given value. }
Index returns a new sodium.Expression that can be used inside a QueryFunc to refer to one of the columns in the table. The ptr must point inside the arguments passed to the QueryFunc.
func Match ¶
func Match[V ~string](ptr *V) struct { Contains func(V) sodium.Expression // matches values that contain the given string. HasPrefix func(V) sodium.Expression // matches values that start with the given string. HasSuffix func(V) sodium.Expression // matches values that end with the given string. }
Match returns a new [MatchExpression] for the given pointer, it can be used inside a QueryFunc to refer to one of the columns in the table. The ptr must point inside the arguments passed to the QueryFunc.
func Merge ¶
func Merge(exprs ...sodium.Expression) sodium.Expression
Merge returns a new [Expression] that can be used inside a QueryFunc to filter for results that match all of the given expressions.
func New ¶
New returns a new sodium.Database. It is suitable for use in tests.
func NewOutput ¶
NewOutput returns a slice of sodium values for the given sodium calculations. A scanner function must be provided, that behaves like a database/sql scanner.
func NewResult ¶
NewResult returns a slice of sodium values for the columns of the given table, a scanner function must be provided, that behaves like a database/sql scanner.
func Order ¶
func Order[V orderable](ptr *V) struct { Increasing func() sodium.Expression // orders values in increasing order. Decreasing func() sodium.Expression // orders values in decreasing order. }
Order returns a new [OrderExpression] for the given pointer, it can be used inside a QueryFunc to refer to one of the columns in the table. The ptr must point inside the arguments passed to the QueryFunc.
func Set ¶
func Set[V any](ptr *V, val V) sodium.Modification
Set returns a new sodium.Modification that can be used inside a PatchFunc to refer to one of the columns in the table. The ptr must point inside the arguments passed to the PatchFunc.
func Slice ¶
func Slice(from, upto int) sodium.Expression
Slice returns a new [RangeExpression] that can be used inside a QueryFunc to limit the affect of the query to a specific range of values. The from and upto values are zero based, and the range is half open, meaning that the value at the from index is included, but the value at the upto index is not.
func Test ¶
Test the implementation of a Database against the SODIUM specification. This function creates new 'testing_' prefixed tables in the database. If the test passes, the testing records are cleaned up. If the test fails, the testing records are left in the database to assist with debugging.
func ValuesOf ¶
ValuesOf destructures a Go value into a set of [Value]s. Nested struct fields and array elements are flattened into a single sequential slice of values. Pointers, complex values maps, functions, and slices will raise a panic if they are encountered (except for []byte). Unexported fields are ignored.
func Where ¶
func Where[V whereable](ptr *V) struct { Min func(V) sodium.Expression // matches values greater than or equal to the given value. Max func(V) sodium.Expression // matches values less than or equal to the given value. MoreThan func(V) sodium.Expression // matches values greater than the given value. LessThan func(V) sodium.Expression // matches values less than the given value. }
Where returns a new [WhereExpression] for the given pointer, it can be used inside a QueryFunc to refer to one of the columns in the table. The ptr must point inside the arguments passed to the QueryFunc.
Types ¶
type Chan ¶
type Chan[K comparable, V any] chan xyz.Trio[K, V, error]
Chan streams results from a Map.Search operation.
type Check ¶
type Check []sodium.Expression
type Counter ¶
type Counter interface {
// contains filtered or unexported methods
}
Counter is a type that can be used inside a StatsFunc to calculate a sum values.
type IncompatibleTypeError ¶
func (IncompatibleTypeError) Error ¶
func (e IncompatibleTypeError) Error() string
type Map ¶
type Map[K comparable, V any] struct { // contains filtered or unexported fields }
Map represents a distinct mapping of data stored in a Database.
func Open ¶
func Open[K comparable, V any](db Database, table Table) Map[K, V]
Open a new Map from the given Database. The table schema is derived from the key and value types 'K' and 'V', following the same rules as ValuesOf. A 'sql' or else, a 'txt' tag controls the name of the column. If no tag is specified, the ToLower(name) of the field is used. If the key is not a struct, the column name is 'id', otherwise it is treated as a composite key across each field. If the value is not a struct, the column name is 'value'. Nested structures are named with an underscore used to seperate the field path unless the structure is embedded, in which case the nested fields are promoted. Arrays elements are suffixed by their index.
func (Map[K, V]) Delete ¶
Delete the value at the specified key in the map if the specified check passes. Boolean returned is true if a value was deleted this way.
func (Map[K, V]) Insert ¶
Insert a new value into the Map at the given key. The given Flag determines how the value is inserted. If the Flag is Upsert, the value will overwrite any existing value at the given key. If the Flag is Create, the value will only be inserted if there is no existing value at the given key, otherwise an error will be returned.
func (Map[K, V]) Lookup ¶
Lookup the specified key in the map and return the value associated with it, if the value is not present in the map, the resulting boolean will be false.
func (Map[K, V]) Mutate ¶
func (m Map[K, V]) Mutate(ctx context.Context, key K, check CheckFunc[V], patch PatchFunc[V]) (bool, error)
Mutate the value at the specified key in the map. The CheckFunc is called with the current value at the specified key, if the CheckFunc returns true, then the PatchFunc is called with the current value at the specified key. The PatchFunc should return the modifications to be made to the value at the specified key.
func (Map[K, V]) UnsafeDelete ¶
UnsafeDelete each value in the map that matches the given query. The number of values that were deleted is returned, along with any error that occurred. The query must include a slice operation that limits the number of values that can be deleted, otherwise the operation will fail. Unsafe because a large amount of data can be permanently deleted this way.
type Patch ¶
type Patch []sodium.Modification
type Query ¶
type Query []sodium.Expression
type QueryFunc ¶
type QueryFunc[K comparable, V any] func(*K, *V) Query
QueryFunc that returns a Query for the given key and value.
type StatsFunc ¶
type StatsFunc[K comparable, V any] func(*K, *V) Stats
StatsFunc that returns a Stats for the given key and value.
type UnsupportedTypeError ¶
func (UnsupportedTypeError) Error ¶
func (e UnsupportedTypeError) Error() string