Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 41 additions & 10 deletions bun.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,78 +8,107 @@ import (
)

type (
Safe = schema.Safe
Name = schema.Name
// Safe marks a SQL fragment as trusted and prevents further escaping.
Safe = schema.Safe
// Name represents a SQL identifier such as a column or table name.
Name = schema.Name
// Ident is a fully qualified SQL identifier.
Ident = schema.Ident
// Order denotes sorting direction used in ORDER BY clauses.
Order = schema.Order

NullTime = schema.NullTime
// NullTime is a nullable time value compatible with Bun.
NullTime = schema.NullTime
// BaseModel provides default metadata embedded into user models.
BaseModel = schema.BaseModel
Query = schema.Query
// Query is implemented by all Bun query builders.
Query = schema.Query

// BeforeAppendModelHook is called before a model is appended to a query.
BeforeAppendModelHook = schema.BeforeAppendModelHook

// BeforeScanRowHook runs before scanning an individual row.
BeforeScanRowHook = schema.BeforeScanRowHook
AfterScanRowHook = schema.AfterScanRowHook
// AfterScanRowHook runs after scanning an individual row.
AfterScanRowHook = schema.AfterScanRowHook
)

const (
OrderAsc = schema.OrderAsc
OrderAscNullsFirst = schema.OrderDesc
OrderAscNullsLast = schema.OrderAscNullsLast
OrderDesc = schema.OrderDesc
// OrderAsc sorts values in ascending order.
OrderAsc = schema.OrderAsc
// OrderAscNullsFirst sorts ascending with NULL values first.
OrderAscNullsFirst = schema.OrderDesc
// OrderAscNullsLast sorts ascending with NULL values last.
OrderAscNullsLast = schema.OrderAscNullsLast
// OrderDesc sorts values in descending order.
OrderDesc = schema.OrderDesc
// OrderDescNullsFirst sorts descending with NULL values first.
OrderDescNullsFirst = schema.OrderDescNullsFirst
OrderDescNullsLast = schema.OrderDescNullsLast
// OrderDescNullsLast sorts descending with NULL values last.
OrderDescNullsLast = schema.OrderDescNullsLast
)

// SafeQuery wraps a raw query string and arguments and marks it safe for Bun.
func SafeQuery(query string, args ...any) schema.QueryWithArgs {
return schema.SafeQuery(query, args)
}

// BeforeSelectHook is invoked before executing SELECT queries.
type BeforeSelectHook interface {
BeforeSelect(ctx context.Context, query *SelectQuery) error
}

// AfterSelectHook is invoked after executing SELECT queries.
type AfterSelectHook interface {
AfterSelect(ctx context.Context, query *SelectQuery) error
}

// BeforeInsertHook is invoked before executing INSERT queries.
type BeforeInsertHook interface {
BeforeInsert(ctx context.Context, query *InsertQuery) error
}

// AfterInsertHook is invoked after executing INSERT queries.
type AfterInsertHook interface {
AfterInsert(ctx context.Context, query *InsertQuery) error
}

// BeforeUpdateHook is invoked before executing UPDATE queries.
type BeforeUpdateHook interface {
BeforeUpdate(ctx context.Context, query *UpdateQuery) error
}

// AfterUpdateHook is invoked after executing UPDATE queries.
type AfterUpdateHook interface {
AfterUpdate(ctx context.Context, query *UpdateQuery) error
}

// BeforeDeleteHook is invoked before executing DELETE queries.
type BeforeDeleteHook interface {
BeforeDelete(ctx context.Context, query *DeleteQuery) error
}

// AfterDeleteHook is invoked after executing DELETE queries.
type AfterDeleteHook interface {
AfterDelete(ctx context.Context, query *DeleteQuery) error
}

// BeforeCreateTableHook is invoked before executing CREATE TABLE queries.
type BeforeCreateTableHook interface {
BeforeCreateTable(ctx context.Context, query *CreateTableQuery) error
}

// AfterCreateTableHook is invoked after executing CREATE TABLE queries.
type AfterCreateTableHook interface {
AfterCreateTable(ctx context.Context, query *CreateTableQuery) error
}

// BeforeDropTableHook is invoked before executing DROP TABLE queries.
type BeforeDropTableHook interface {
BeforeDropTable(ctx context.Context, query *DropTableQuery) error
}

// AfterDropTableHook is invoked after executing DROP TABLE queries.
type AfterDropTableHook interface {
AfterDropTable(ctx context.Context, query *DropTableQuery) error
}
Expand All @@ -89,10 +118,12 @@ func SetLogger(logger internal.Logging) {
internal.SetLogger(logger)
}

// In wraps a slice so it can be used with the IN clause.
func In(slice any) schema.QueryAppender {
return schema.In(slice)
}

// NullZero forces zero values to be treated as NULL when building queries.
func NullZero(value any) schema.QueryAppender {
return schema.NullZero(value)
}
10 changes: 10 additions & 0 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ const (
discardUnknownColumns internal.Flag = 1 << iota
)

// DBStats tracks aggregate query counters collected by Bun.
type DBStats struct {
Queries uint32
Errors uint32
}

// DBOption mutates DB configuration during construction.
type DBOption func(db *DB)

// WithOptions applies multiple DBOption values at once.
func WithOptions(opts ...DBOption) DBOption {
return func(db *DB) {
for _, opt := range opts {
Expand All @@ -34,6 +37,7 @@ func WithOptions(opts ...DBOption) DBOption {
}
}

// WithDiscardUnknownColumns ignores columns returned by queries that are not present in models.
func WithDiscardUnknownColumns() DBOption {
return func(db *DB) {
db.flags = db.flags.Set(discardUnknownColumns)
Expand All @@ -46,12 +50,14 @@ type ConnResolver interface {
Close() error
}

// WithConnResolver registers a connection resolver that chooses a connection per query.
func WithConnResolver(resolver ConnResolver) DBOption {
return func(db *DB) {
db.resolver = resolver
}
}

// DB is the central access point for building and executing Bun queries.
type DB struct {
// Must be a pointer so we copy the whole state, not individual fields.
*noCopyState
Expand All @@ -73,6 +79,7 @@ type noCopyState struct {
stats DBStats
}

// NewDB wraps an existing *sql.DB with Bun using the given dialect and options.
func NewDB(sqldb *sql.DB, dialect schema.Dialect, opts ...DBOption) *DB {
dialect.Init(sqldb)

Expand Down Expand Up @@ -355,6 +362,7 @@ func (db *DB) format(query string, args []any) string {

//------------------------------------------------------------------------------

// Conn wraps *sql.Conn so queries continue to use Bun features and hooks.
type Conn struct {
db *DB
*sql.Conn
Expand Down Expand Up @@ -501,6 +509,7 @@ func (c Conn) BeginTx(ctx context.Context, opts *sql.TxOptions) (Tx, error) {

//------------------------------------------------------------------------------

// Stmt wraps *sql.Stmt so prepared statements participate in Bun logging.
type Stmt struct {
*sql.Stmt
}
Expand All @@ -519,6 +528,7 @@ func (db *DB) PrepareContext(ctx context.Context, query string) (Stmt, error) {

//------------------------------------------------------------------------------

// Tx wraps *sql.Tx and preserves Bun-specific context such as hooks and dialect.
type Tx struct {
ctx context.Context
db *DB
Expand Down
3 changes: 3 additions & 0 deletions hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"unicode"
)

// QueryEvent captures information about a query execution for hooks.
type QueryEvent struct {
DB *DB

Expand All @@ -25,6 +26,7 @@ type QueryEvent struct {
Stash map[any]any
}

// Operation returns the SQL operation name such as SELECT or UPDATE.
func (e *QueryEvent) Operation() string {
if e.IQuery != nil {
return e.IQuery.Operation()
Expand All @@ -44,6 +46,7 @@ func queryOperation(query string) string {
return queryOp
}

// QueryHook allows observing queries before and after execution.
type QueryHook interface {
BeforeQuery(context.Context, *QueryEvent) context.Context
AfterQuery(context.Context, *QueryEvent)
Expand Down
2 changes: 2 additions & 0 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ var (
bytesType = reflect.TypeFor[[]byte]()
)

// Model is implemented by all Bun models.
type Model = schema.Model

type rowScanner interface {
ScanRow(ctx context.Context, rows *sql.Rows) error
}

// TableModel describes models that map to database tables and support query lifecycle hooks.
type TableModel interface {
Model

Expand Down
5 changes: 4 additions & 1 deletion query_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
allWithDeletedFlag
)

// WithQuery defines a common table expression used by another query.
type WithQuery struct {
name string
query Query
Expand Down Expand Up @@ -76,7 +77,7 @@ var (
_ IDB = (*Tx)(nil)
)

// QueryBuilder is used for common query methods
// QueryBuilder exposes shared filtering helpers across query builders.
type QueryBuilder interface {
Query
Where(query string, args ...any) QueryBuilder
Expand Down Expand Up @@ -257,6 +258,7 @@ func (q *baseQuery) isSoftDelete() bool {

//------------------------------------------------------------------------------

// NewWithQuery creates a CTE with the given name and underlying query.
func NewWithQuery(name string, query Query) *WithQuery {
return &WithQuery{
name: name,
Expand Down Expand Up @@ -1568,6 +1570,7 @@ func (q *orderLimitOffsetQuery) appendLimitOffset(gen schema.QueryGen, b []byte)
return b, nil
}

// IsReadOnlyQuery reports whether the provided query and its CTEs are SELECT-only.
func IsReadOnlyQuery(query Query) bool {
sel, ok := query.(*SelectQuery)
if !ok {
Expand Down
2 changes: 2 additions & 0 deletions query_column_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/uptrace/bun/schema"
)

// AddColumnQuery builds ALTER TABLE ... ADD COLUMN statements.
type AddColumnQuery struct {
baseQuery

Expand All @@ -19,6 +20,7 @@ type AddColumnQuery struct {

var _ Query = (*AddColumnQuery)(nil)

// NewAddColumnQuery creates an AddColumnQuery bound to the provided DB.
func NewAddColumnQuery(db *DB) *AddColumnQuery {
q := &AddColumnQuery{
baseQuery: baseQuery{
Expand Down
2 changes: 2 additions & 0 deletions query_column_drop.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/uptrace/bun/schema"
)

// DropColumnQuery builds ALTER TABLE ... DROP COLUMN statements.
type DropColumnQuery struct {
baseQuery

Expand All @@ -17,6 +18,7 @@ type DropColumnQuery struct {

var _ Query = (*DropColumnQuery)(nil)

// NewDropColumnQuery creates a DropColumnQuery bound to the provided DB.
func NewDropColumnQuery(db *DB) *DropColumnQuery {
q := &DropColumnQuery{
baseQuery: baseQuery{
Expand Down
2 changes: 2 additions & 0 deletions query_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/uptrace/bun/schema"
)

// DeleteQuery builds SQL DELETE statements.
type DeleteQuery struct {
whereBaseQuery
orderLimitOffsetQuery
Expand All @@ -21,6 +22,7 @@ type DeleteQuery struct {

var _ Query = (*DeleteQuery)(nil)

// NewDeleteQuery returns a DeleteQuery associated with the provided DB.
func NewDeleteQuery(db *DB) *DeleteQuery {
q := &DeleteQuery{
whereBaseQuery: whereBaseQuery{
Expand Down
2 changes: 2 additions & 0 deletions query_index_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/uptrace/bun/schema"
)

// CreateIndexQuery builds CREATE INDEX statements.
type CreateIndexQuery struct {
whereBaseQuery

Expand All @@ -25,6 +26,7 @@ type CreateIndexQuery struct {

var _ Query = (*CreateIndexQuery)(nil)

// NewCreateIndexQuery returns a CreateIndexQuery tied to the provided DB.
func NewCreateIndexQuery(db *DB) *CreateIndexQuery {
q := &CreateIndexQuery{
whereBaseQuery: whereBaseQuery{
Expand Down
2 changes: 2 additions & 0 deletions query_index_drop.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/uptrace/bun/schema"
)

// DropIndexQuery builds DROP INDEX statements.
type DropIndexQuery struct {
baseQuery
cascadeQuery
Expand All @@ -21,6 +22,7 @@ type DropIndexQuery struct {

var _ Query = (*DropIndexQuery)(nil)

// NewDropIndexQuery returns a DropIndexQuery associated with the provided DB.
func NewDropIndexQuery(db *DB) *DropIndexQuery {
q := &DropIndexQuery{
baseQuery: baseQuery{
Expand Down
2 changes: 2 additions & 0 deletions query_insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/uptrace/bun/schema"
)

// InsertQuery builds SQL INSERT statements.
type InsertQuery struct {
whereBaseQuery
returningQuery
Expand All @@ -27,6 +28,7 @@ type InsertQuery struct {

var _ Query = (*InsertQuery)(nil)

// NewInsertQuery returns an InsertQuery tied to the provided DB.
func NewInsertQuery(db *DB) *InsertQuery {
q := &InsertQuery{
whereBaseQuery: whereBaseQuery{
Expand Down
2 changes: 2 additions & 0 deletions query_merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/uptrace/bun/schema"
)

// MergeQuery builds MERGE statements for dialects that support them.
type MergeQuery struct {
baseQuery
returningQuery
Expand All @@ -23,6 +24,7 @@ type MergeQuery struct {

var _ Query = (*MergeQuery)(nil)

// NewMergeQuery creates a MergeQuery associated with the provided DB.
func NewMergeQuery(db *DB) *MergeQuery {
q := &MergeQuery{
baseQuery: baseQuery{
Expand Down
Loading
Loading