qubr

package module
v0.0.0-...-07ce7ff Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2024 License: MIT Imports: 10 Imported by: 0

README

qubr

A query builder for Go with a querying and data mapping mechanism. The SQL is not hidden from the caller, and we embrace understanding of what is going on under the hood. Instead of engineering a solution that poorly fits everyone, qubr's goal is to fit a specific purpose of providing a good toolkit of useful, simple 80%+ use case helpers, and a seamless way around not having to use these tools for that other ~20% of the time.

Example

Many SQL queries are just simple select queries, so this will be your bread and butter.

package main

import (
	"database/sql"
	"fmt"
	"github.com/aliics/qubr"
)

type user struct {
	ID    uint64
	Email string
}

func main() {
	// Your SQL database connection.
	db, err := sql.Open("driverName", "sourceName")
	if err != nil {
		panic(err)
	}

	// SELECT "ID", "Email"
	// FROM "user" 
	// WHERE "Email" = '100'
	// LIMIT 100;
	users, err := qubr.Select[user]().
		Where(qubr.Equal("Email", "ollie@example.org")).
		Limit(100).
		Query(db) // Maps the resulting rows to a "user".
	if err != nil {
		panic(err)
	}

	fmt.Printf("users found: %v\n", users)
}

There are functions for DELETE FROM, INSERT INTO, and UPDATE ... SET statements as well. If you cannot neatly construct your queries using these functions, we also support using the QueryContext function and providing a query. Like with Query in the example, the rows will be mapped to your struct.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrTableNameAlreadySet = errors.New("table name has already been set")

	ErrDoubleWhereClause  = errors.New("where clause is already present")
	ErrMissingWhereClause = errors.New("where clause is not yet present")

	ErrLimitAlreadySet = errors.New("limit value has already been set")

	ErrNoInsertValues = errors.New("insert statement has no insert values")

	ErrNoSetStatement = errors.New("update statement has no insert values")

	ErrNoRows = errors.New("get resulted in no rows")
)

Functions

func QueryContext

func QueryContext[T any](ctx context.Context, db *sql.DB, query string, args ...any) ([]T, error)

QueryContext is a wrapper for sql.DB's QueryContext function. The rows are mapped to T, where each field of T is a column in the row.

func SetupTestDatabase

func SetupTestDatabase(t *testing.T, setupQueries ...string) *sql.DB

Types

type DeleteBuilder

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

DeleteBuilder is a QueryBuilder for building SQL DELETE queries. Utilizing the related Delete functions, you can construct these queries. Example:

result, err := Delete[User]().
	Where(Equal("ID", 42)).
	ExecContext(ctx, db) // Or BuildQuery to use the raw SQL.
if err != nil {
	return err
}

func Delete

func Delete[T any]() DeleteBuilder[T]

Delete will construct a new DeleteBuilder, and the table name will be set based on the type given.

func (DeleteBuilder[T]) And

func (b DeleteBuilder[T]) And(op FieldOperation) DeleteBuilder[T]

And will apply an AND to the existing where clause. DeleteBuilder.Where must be called before this.

func (DeleteBuilder[T]) BuildQuery

func (b DeleteBuilder[T]) BuildQuery() (query string, args []any, err error)

BuildQuery will construct the SQL query DeleteBuilder is currently representing. User input will utilize placeholders, and the values of the input will be in the 2nd return value, args. If there was an issue in the construction of DeleteBuilder, then the 3rd return value, err will not non-nil.

The resulting query should look something like:

DELETE FROM "schema"."table" WHERE "field1" = ? LIMIT ?;

func (DeleteBuilder[T]) Exec

func (b DeleteBuilder[T]) Exec(db *sql.DB) (sql.Result, error)

Exec wraps DeleteBuilder.ExecContext, which will execute the delete query represented by the DeleteBuilder.

func (DeleteBuilder[T]) ExecContext

func (b DeleteBuilder[T]) ExecContext(ctx context.Context, db *sql.DB) (sql.Result, error)

ExecContext will execute the delete query represented by DeleteBuilder. This will execute using the provided sql.DB, and the response is simply passed back.

func (DeleteBuilder[T]) From

func (b DeleteBuilder[T]) From(tableName string) DeleteBuilder[T]

From will explicitly set the table name. This cannot be called more once.

func (DeleteBuilder[T]) Limit

func (b DeleteBuilder[T]) Limit(n uint64) DeleteBuilder[T]

Limit will apply a limit to the select statement. Limiting the number of rows resulting from your table. This cannot be called more than once.

func (DeleteBuilder[T]) Or

Or will apply an OR to the existing where clause. DeleteBuilder.Where must be called before this.

func (DeleteBuilder[T]) Where

func (b DeleteBuilder[T]) Where(op FieldOperation) DeleteBuilder[T]

Where will apply a FieldOperation as the initial comparison operator of a where clause. Where cannot be called more than once, use DeleteBuilder.And or DeleteBuilder.Or for further filtering.

type ErrInvalidTableName

type ErrInvalidTableName struct {
	Name string
}

ErrInvalidTableName occurs when a string provided cannot be used as a table name.

func (ErrInvalidTableName) Error

func (e ErrInvalidTableName) Error() string

type ErrUnknownFieldName

type ErrUnknownFieldName struct {
	Name string
}

ErrUnknownFieldName occurs when a string provided cannot be used as a field name.

func (ErrUnknownFieldName) Error

func (e ErrUnknownFieldName) Error() string

type FieldOperation

type FieldOperation struct {
	Operator Operator

	FieldName string
	ValueRaw  any
}

FieldOperation represents some field comparison operation utilizing an Operator. It is not recommended to construct a FieldOperation directly, instead, use one of the constructing functions like Equal or In.

func Equal

func Equal(field string, v any) FieldOperation

Equal is a wrapper for constructing a FieldOperation with an OperatorEqual passed in. Equivalent SQL will be:

"field" = ?

func GreaterThan

func GreaterThan(field string, v any) FieldOperation

GreaterThan is a wrapper for constructing a FieldOperation with an OperatorGreaterThan passed in. Equivalent SQL will be:

"field" > ?

func GreaterThanOrEqual

func GreaterThanOrEqual(field string, v any) FieldOperation

GreaterThanOrEqual is a wrapper for constructing a FieldOperation with an OperatorGreaterThanOrEqual passed in. Equivalent SQL will be:

"field" >= ?

func In

func In(field string, values ...any) FieldOperation

In is a wrapper for constructing a FieldOperation with an OperatorIn passed in. Equivalent SQL will be:

"field" IN (?, ...)

func IsFalse

func IsFalse(field string) FieldOperation

IsFalse is a wrapper for constructing a FieldOperation with an OperatorIsFalse passed in. Since it's just a boolean comparison, we utilize Equal to do this. Equivalent SQL will be:

"field" = FALSE

func IsTrue

func IsTrue(field string) FieldOperation

IsTrue is a wrapper for constructing a FieldOperation with an OperatorIsTrue passed in. Since it's just a boolean comparison, we utilize Equal to do this. Equivalent SQL will be:

"field" = TRUE

func LessThan

func LessThan(field string, v any) FieldOperation

LessThan is a wrapper for constructing a FieldOperation with an OperatorLessThan passed in. Equivalent SQL will be:

"field" < ?

func LessThanOrEqual

func LessThanOrEqual(field string, v any) FieldOperation

LessThanOrEqual is a wrapper for constructing a FieldOperation with an OperatorLessThanOrEqual passed in. Equivalent SQL will be:

"field" <= ?

func NotEqual

func NotEqual(field string, v any) FieldOperation

NotEqual is a wrapper for constructing a FieldOperation with an OperatorNotEqual passed in. Equivalent SQL will be:

"field" != ?

func NotIn

func NotIn(field string, values ...any) FieldOperation

NotIn is a wrapper for constructing a FieldOperation with an OperatorNotIn passed in. Equivalent SQL will be:

"field" NOT IN (?, ...)

type InsertBuilder

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

InsertBuilder is a QueryBuilder for building SQL INSERT queries. Utilizing the related Insert functions, you can construct these queries. Example:

result, err := Insert[User]().
	Values(User{ID: 42, Name: "Alex"}).
	ExecContext(ctx, db) // Or BuildQuery to use the raw SQL.
if err != nil {
	return err
}

func Insert

func Insert[T any]() InsertBuilder[T]

Insert will construct a new InsertBuilder, and the table name will be set based on the type given.

func (InsertBuilder[T]) BuildQuery

func (b InsertBuilder[T]) BuildQuery() (query string, args []any, err error)

BuildQuery will construct the SQL query InsertBuilder is currently representing. User input will utilize placeholders, and the values of the input will be in the 2nd return value, args. If there was an issue in the construction of InsertBuilder, then the 3rd return value, err will not non-nil.

The resulting query should look something like:

INSERT INTO "table" VALUES (?, ?, ?);

func (InsertBuilder[T]) Exec

func (b InsertBuilder[T]) Exec(db *sql.DB) (sql.Result, error)

Exec wraps InsertBuilder.ExecContext, which will execute the insert query represented by the InsertBuilder.

func (InsertBuilder[T]) ExecContext

func (b InsertBuilder[T]) ExecContext(ctx context.Context, db *sql.DB) (sql.Result, error)

ExecContext will execute the insert query represented by the InsertBuilder. This will execute using the provided sql.DB, and the response is simply passed back.

func (InsertBuilder[T]) Into

func (b InsertBuilder[T]) Into(tableName string) InsertBuilder[T]

Into will explicitly set the table name. This cannot be called more once.

func (InsertBuilder[T]) Values

func (b InsertBuilder[T]) Values(t ...T) InsertBuilder[T]

Values will represent the values to be inserted into your table. Each struct given being a row, and its fields being the columns.

type Operator

type Operator uint8

Operator is a type representing one of the various comparison operators in ANSI SQL (ISO 9075).

const (
	OperatorEqual Operator = iota
	OperatorNotEqual
	OperatorGreaterThan
	OperatorLessThan
	OperatorGreaterThanOrEqual
	OperatorLessThanOrEqual
	OperatorIn
	OperatorNotIn
)

func (Operator) String

func (o Operator) String() string

type QueryBuilder

type QueryBuilder interface {
	BuildQuery() (query string, args []any, err error)
}

type SelectBuilder

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

SelectBuilder is a QueryBuilder for building SQL SELECT queries. Utilizing the related Select functions, you can construct these queries. Example:

users, err := Select[User]().
	Where(Equal("ID", 42)).
	QueryContext(ctx, db) // Or BuildQuery to use the raw SQL.
if err != nil {
	return err
}

func Select

func Select[T any]() SelectBuilder[T]

Select will construct a new SelectBuilder, and the table name will be set based on the type given.

func (SelectBuilder[T]) And

func (b SelectBuilder[T]) And(op FieldOperation) SelectBuilder[T]

And will apply an AND to the existing where clause. SelectBuilder.Where must be called before this.

func (SelectBuilder[T]) BuildQuery

func (b SelectBuilder[T]) BuildQuery() (query string, args []any, err error)

BuildQuery will construct the SQL query SelectBuilder is currently representing. User input will utilize placeholders, and the values of the input will be in the 2nd return value, args. If there was an issue in the construction of SelectBuilder, then the 3rd return value, err will not non-nil.

The resulting query should look something like:

SELECT "field1", "field2" FROM "schema"."table" WHERE "field1" = ? LIMIT ?;

func (SelectBuilder[T]) From

func (b SelectBuilder[T]) From(tableName string) SelectBuilder[T]

From will explicitly set the table name. This cannot be called more once.

func (SelectBuilder[T]) GetOne

func (b SelectBuilder[T]) GetOne(db *sql.DB) (*T, error)

GetOne wraps SelectBuilder.GetOneContext, this will use the query represented by SelectBuilder. There is the expectation that at least one result is returned. The first result will be mapped to T.

func (SelectBuilder[T]) GetOneContext

func (b SelectBuilder[T]) GetOneContext(ctx context.Context, db *sql.DB) (*T, error)

GetOneContext will use the query represented by the SelectBuilder, utilizing the sql.DB provided. There is the expectation that at least one result is returned. The first result will be mapped to T.

func (SelectBuilder[T]) Limit

func (b SelectBuilder[T]) Limit(n uint64) SelectBuilder[T]

Limit will apply a limit to the select statement. Limiting the number of rows resulting from your table. This cannot be called more than once.

func (SelectBuilder[T]) Or

Or will apply an OR to the existing where clause. SelectBuilder.Where must be called before this.

func (SelectBuilder[T]) Query

func (b SelectBuilder[T]) Query(db *sql.DB) ([]T, error)

Query wraps SelectBuilder.QueryContext, this will use the query represented by SelectBuilder. The row results are all mapped to T.

func (SelectBuilder[T]) QueryContext

func (b SelectBuilder[T]) QueryContext(ctx context.Context, db *sql.DB) ([]T, error)

QueryContext will use the query represented by the SelectBuilder, utilizing the sql.DB provided. The results are all mapped to T.

func (SelectBuilder[T]) Where

func (b SelectBuilder[T]) Where(op FieldOperation) SelectBuilder[T]

Where will apply a FieldOperation as the initial comparison operator of a where clause. Where cannot be called more than once, use SelectBuilder.And or SelectBuilder.Or for further filtering.

func (SelectBuilder[T]) WithFields

func (b SelectBuilder[T]) WithFields(names ...string) SelectBuilder[T]

WithFields allows the selection of very specific fields, instead of all fields in the struct. The field needs to exist on the struct, and it has to be the name we will use in the query.

type UpdateBuilder

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

UpdateBuilder is a QueryBuilder for building SQL UPDATE queries. Utilizing the related Update functions, you can construct these queries. Example:

result, err := Update[User]().
	SetStruct(User{ID: 42, Name: "Alex"}).
	Where(Equal("ID", 42)).
	ExecContext(ctx, db) // Or BuildQuery to use the raw SQL.
if err != nil {
	return err
}

func Update

func Update[T any]() UpdateBuilder[T]

Update will construct a new UpdateBuilder, and the table name will be set based on the type given.

func (UpdateBuilder[T]) And

func (b UpdateBuilder[T]) And(op FieldOperation) UpdateBuilder[T]

And will apply an AND to the existing where clause. UpdateBuilder.Where must be called before this.

func (UpdateBuilder[T]) BuildQuery

func (b UpdateBuilder[T]) BuildQuery() (query string, args []any, err error)

BuildQuery will construct the SQL query UpdateBuilder is currently representing. User input will utilize placeholders, and the values of the input will be in the 2nd return value, args. If there was an issue in the construction of UpdateBuilder, then the 3rd return value, err will not non-nil.

The resulting query should look something like:

UPDATE "table" SET "field1" = ?, "field2" = ? WHERE "field1" = ?;

func (UpdateBuilder[T]) Exec

func (b UpdateBuilder[T]) Exec(db *sql.DB) (sql.Result, error)

Exec wraps UpdateBuilder.ExecContext, which will execute the update query represented by the UpdateBuilder.

func (UpdateBuilder[T]) ExecContext

func (b UpdateBuilder[T]) ExecContext(ctx context.Context, db *sql.DB) (sql.Result, error)

ExecContext will execute the update query represented by the UpdateBuilder. This will execute using the provided sql.DB, and the response is simply passed back.

func (UpdateBuilder[T]) Into

func (b UpdateBuilder[T]) Into(tableName string) UpdateBuilder[T]

Into will explicitly set the table name. This cannot be called more once.

func (UpdateBuilder[T]) Or

Or will apply an OR to the existing where clause. UpdateBuilder.Where must be called before this.

func (UpdateBuilder[T]) SetStruct

func (b UpdateBuilder[T]) SetStruct(t T) UpdateBuilder[T]

func (UpdateBuilder[T]) Where

func (b UpdateBuilder[T]) Where(op FieldOperation) UpdateBuilder[T]

Where will apply a FieldOperation as the initial comparison operator of a where clause. Where cannot be called more than once, use UpdateBuilder.And or UpdateBuilder.Or for further filtering.

Jump to

Keyboard shortcuts

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