chore: ⬆️ updated deps

This commit is contained in:
2026-05-20 22:52:20 +02:00
parent d9f27c1775
commit 43f4680176
374 changed files with 295527 additions and 301467 deletions
+29
View File
@@ -1,3 +1,32 @@
## [1.2.18](github.com/uptrace/bun/compare/v1.2.17...v1.2.18) (2026-02-28)
### Bug Fixes
* handle []byte and [N]byte in Tuple, separate List from Tuple imp… ([#1340](/github.com/uptrace/bun/issues/1340)) ([bec98b9](github.com/uptrace/bun/commits/bec98b975ad093cd34c0a7821120350e475b901b))
* validate parenthesized content in ReadIdentifier to prevent ?(?, ?) misparse ([#1338](/github.com/uptrace/bun/issues/1338)) ([b8da15b](github.com/uptrace/bun/commits/b8da15b391fd3b78f543125dc574ad32a240780c)), closes [#1337](github.com/uptrace/bun/issues/1337)
## [1.2.17](github.com/uptrace/bun/compare/v1.2.16...v1.2.17) (2026-02-21)
### Bug Fixes
* migrator exec error propagation ([#1320](/github.com/uptrace/bun/issues/1320)) ([b40f603](github.com/uptrace/bun/commits/b40f603ffc4595894c2178ab1841122abaead50d))
* OrderAscNullsFirst mapping (fixes [#1305](/github.com/uptrace/bun/issues/1305)) ([43b6af2](github.com/uptrace/bun/commits/43b6af233feebed72a911b03dbccd63247c262de))
* panic in indirectAsKey when loading complex models. TypeOf(v) returns nil ([2788c5b](github.com/uptrace/bun/commits/2788c5b2e052b21cb1abf9af2a6da8a6ad43993c))
* RunMigration marks migration as applied after running ([#1330](/github.com/uptrace/bun/issues/1330)) ([990c2eb](github.com/uptrace/bun/commits/990c2ebf34e3e2c59979943fabdda2892c2c3dcc))
### Features
* add Tuple and List ([#1331](/github.com/uptrace/bun/issues/1331)) ([5c2b3d1](github.com/uptrace/bun/commits/5c2b3d150688ca6861d20fd590ea20cd415749a3))
* create unique index on migration name column in Migrator.Init ([#1332](/github.com/uptrace/bun/issues/1332)) ([44ac056](github.com/uptrace/bun/commits/44ac056ac35dc396dcbb09252ce69f1ccd9b0250))
* **update:** use DEFAULT instead of NULL on databases that support it ([#1315](/github.com/uptrace/bun/issues/1315)) ([cabcffd](github.com/uptrace/bun/commits/cabcffd47e35141064e4e6e15ea97675393098a0))
## [1.2.16](github.com/uptrace/bun/compare/v1.2.15...v1.2.16) (2025-11-20)
+160 -10
View File
@@ -2,84 +2,115 @@ package bun
import (
"context"
"fmt"
"reflect"
"github.com/uptrace/bun/internal"
"github.com/uptrace/bun/schema"
)
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.OrderAscNullsFirst
// 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
}
@@ -89,10 +120,129 @@ func SetLogger(logger internal.Logging) {
internal.SetLogger(logger)
}
// In wraps a slice so it can be used with the IN clause.
//
// Deprecated: Use bun.List or bun.Tuple instead.
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)
}
//------------------------------------------------------------------------------
// ListValues formats a Go slice as a comma-separated SQL list (e.g., "1, 2, 3").
type ListValues struct {
slice any
}
var _ schema.QueryAppender = ListValues{}
// List creates a ListValues from a Go slice for use in SQL IN expressions.
func List(slice any) ListValues {
return ListValues{
slice: slice,
}
}
// AppendQuery appends the comma-separated list values to the byte slice.
func (in ListValues) AppendQuery(gen schema.QueryGen, b []byte) (_ []byte, err error) {
v := reflect.ValueOf(in.slice)
if v.Kind() != reflect.Slice {
return nil, fmt.Errorf("ch: List(non-slice %T)", in.slice)
}
b = appendValues(gen, b, v)
return b, nil
}
func appendValues(gen schema.QueryGen, b []byte, slice reflect.Value) []byte {
sliceLen := slice.Len()
if sliceLen == 0 {
return append(b, "NULL"...)
}
for i := range sliceLen {
if i > 0 {
b = append(b, ", "...)
}
elem := slice.Index(i)
if elem.Kind() == reflect.Interface {
elem = elem.Elem()
}
b = gen.AppendValue(b, elem)
}
return b
}
//------------------------------------------------------------------------------
// TupleValues formats a Go slice as a parenthesized SQL tuple (e.g., "(1, 2, 3)").
type TupleValues struct {
slice any
}
var _ schema.QueryAppender = TupleValues{}
// Tuple creates a TupleValues from a slice for use in SQL expressions.
func Tuple(slice any) TupleValues {
return TupleValues{
slice: slice,
}
}
// AppendQuery appends the parenthesized tuple to the byte slice.
func (in TupleValues) AppendQuery(gen schema.QueryGen, b []byte) (_ []byte, err error) {
v := reflect.ValueOf(in.slice)
if !v.IsValid() {
b = append(b, "(NULL)"...)
return b, nil
}
if v.Kind() != reflect.Slice {
return nil, fmt.Errorf("ch: Tuple(non-slice %T)", in.slice)
}
b = append(b, '(')
b = appendTuples(gen, b, v)
b = append(b, ')')
return b, nil
}
func appendTuples(gen schema.QueryGen, b []byte, slice reflect.Value) []byte {
sliceLen := slice.Len()
if sliceLen == 0 {
return append(b, "NULL"...)
}
for i := range sliceLen {
if i > 0 {
b = append(b, ", "...)
}
elem := slice.Index(i)
if elem.Kind() == reflect.Interface {
elem = elem.Elem()
}
switch elem.Kind() {
case reflect.Array, reflect.Slice:
if elem.Type().Elem().Kind() == reflect.Uint8 {
b = gen.AppendValue(b, elem)
} else {
b = append(b, '(')
b = appendTuples(gen, b, elem)
b = append(b, ')')
}
default:
b = gen.AppendValue(b, elem)
}
}
return b
}
+103
View File
@@ -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 {
@@ -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)
@@ -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
@@ -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)
@@ -91,6 +98,7 @@ func NewDB(sqldb *sql.DB, dialect schema.Dialect, opts ...DBOption) *DB {
return db
}
// String returns a string representation of the DB showing its dialect.
func (db *DB) String() string {
var b strings.Builder
b.WriteString("DB<dialect=")
@@ -99,6 +107,8 @@ func (db *DB) String() string {
return b.String()
}
// Close closes the database connection and any registered connection resolver.
// It returns the first error encountered during closure.
func (db *DB) Close() error {
if db.closed.Swap(true) {
return nil
@@ -115,6 +125,7 @@ func (db *DB) Close() error {
return firstErr
}
// DBStats returns aggregated query statistics including total queries and errors.
func (db *DB) DBStats() DBStats {
return DBStats{
Queries: atomic.LoadUint32(&db.stats.Queries),
@@ -122,62 +133,78 @@ func (db *DB) DBStats() DBStats {
}
}
// NewValues creates a VALUES query for inserting multiple rows efficiently.
func (db *DB) NewValues(model any) *ValuesQuery {
return NewValuesQuery(db, model)
}
// NewMerge creates a MERGE (UPSERT) query for insert-or-update operations.
func (db *DB) NewMerge() *MergeQuery {
return NewMergeQuery(db)
}
// NewSelect creates a SELECT query builder.
func (db *DB) NewSelect() *SelectQuery {
return NewSelectQuery(db)
}
// NewInsert creates an INSERT query builder.
func (db *DB) NewInsert() *InsertQuery {
return NewInsertQuery(db)
}
// NewUpdate creates an UPDATE query builder.
func (db *DB) NewUpdate() *UpdateQuery {
return NewUpdateQuery(db)
}
// NewDelete creates a DELETE query builder.
func (db *DB) NewDelete() *DeleteQuery {
return NewDeleteQuery(db)
}
// NewRaw creates a raw SQL query with the given query string and arguments.
func (db *DB) NewRaw(query string, args ...any) *RawQuery {
return NewRawQuery(db, query, args...)
}
// NewCreateTable creates a CREATE TABLE DDL query builder.
func (db *DB) NewCreateTable() *CreateTableQuery {
return NewCreateTableQuery(db)
}
// NewDropTable creates a DROP TABLE DDL query builder.
func (db *DB) NewDropTable() *DropTableQuery {
return NewDropTableQuery(db)
}
// NewCreateIndex creates a CREATE INDEX DDL query builder.
func (db *DB) NewCreateIndex() *CreateIndexQuery {
return NewCreateIndexQuery(db)
}
// NewDropIndex creates a DROP INDEX DDL query builder.
func (db *DB) NewDropIndex() *DropIndexQuery {
return NewDropIndexQuery(db)
}
// NewTruncateTable creates a TRUNCATE TABLE DDL query builder.
func (db *DB) NewTruncateTable() *TruncateTableQuery {
return NewTruncateTableQuery(db)
}
// NewAddColumn creates an ALTER TABLE ADD COLUMN DDL query builder.
func (db *DB) NewAddColumn() *AddColumnQuery {
return NewAddColumnQuery(db)
}
// NewDropColumn creates an ALTER TABLE DROP COLUMN DDL query builder.
func (db *DB) NewDropColumn() *DropColumnQuery {
return NewDropColumnQuery(db)
}
// ResetModel drops and recreates tables for the given models.
// This is useful for testing and development but should not be used in production.
func (db *DB) ResetModel(ctx context.Context, models ...any) error {
for _, model := range models {
if _, err := db.NewDropTable().Model(model).IfExists().Cascade().Exec(ctx); err != nil {
@@ -190,10 +217,13 @@ func (db *DB) ResetModel(ctx context.Context, models ...any) error {
return nil
}
// Dialect returns the database dialect being used (e.g., PostgreSQL, MySQL, SQLite).
func (db *DB) Dialect() schema.Dialect {
return db.dialect
}
// ScanRows scans all rows from the result set into the destination values.
// It closes the rows when complete.
func (db *DB) ScanRows(ctx context.Context, rows *sql.Rows, dest ...any) error {
defer rows.Close()
@@ -210,6 +240,7 @@ func (db *DB) ScanRows(ctx context.Context, rows *sql.Rows, dest ...any) error {
return rows.Err()
}
// ScanRow scans a single row from the result set into the destination values.
func (db *DB) ScanRow(ctx context.Context, rows *sql.Rows, dest ...any) error {
model, err := newModel(db, dest)
if err != nil {
@@ -224,6 +255,7 @@ func (db *DB) ScanRow(ctx context.Context, rows *sql.Rows, dest ...any) error {
return rs.ScanRow(ctx, rows)
}
// Table returns the schema table metadata for the given type.
func (db *DB) Table(typ reflect.Type) *schema.Table {
return db.dialect.Tables().Get(typ)
}
@@ -234,6 +266,7 @@ func (db *DB) RegisterModel(models ...any) {
db.dialect.Tables().Register(models...)
}
// clone creates a shallow copy of the DB with independent query hooks.
func (db *DB) clone() *DB {
clone := *db
@@ -253,6 +286,7 @@ func (db *DB) WithNamedArg(name string, value any) *DB {
return clone
}
// QueryGen returns the query generator used for formatting SQL queries.
func (db *DB) QueryGen() schema.QueryGen {
return db.gen
}
@@ -309,10 +343,15 @@ func (db *DB) HasFeature(feat feature.Feature) bool {
//------------------------------------------------------------------------------
// Exec executes a query without returning rows using a background context.
// Arguments are formatted using the dialect's placeholder syntax.
func (db *DB) Exec(query string, args ...any) (sql.Result, error) {
return db.ExecContext(context.Background(), query, args...)
}
// ExecContext executes a query without returning rows.
// Arguments are formatted using the dialect's placeholder syntax.
// Query hooks are invoked before and after execution.
func (db *DB) ExecContext(
ctx context.Context, query string, args ...any,
) (sql.Result, error) {
@@ -323,10 +362,15 @@ func (db *DB) ExecContext(
return res, err
}
// Query executes a query returning rows using a background context.
// Arguments are formatted using the dialect's placeholder syntax.
func (db *DB) Query(query string, args ...any) (*sql.Rows, error) {
return db.QueryContext(context.Background(), query, args...)
}
// QueryContext executes a query returning rows.
// Arguments are formatted using the dialect's placeholder syntax.
// Query hooks are invoked before and after execution.
func (db *DB) QueryContext(
ctx context.Context, query string, args ...any,
) (*sql.Rows, error) {
@@ -337,10 +381,15 @@ func (db *DB) QueryContext(
return rows, err
}
// QueryRow executes a query expected to return at most one row using a background context.
// Arguments are formatted using the dialect's placeholder syntax.
func (db *DB) QueryRow(query string, args ...any) *sql.Row {
return db.QueryRowContext(context.Background(), query, args...)
}
// QueryRowContext executes a query expected to return at most one row.
// Arguments are formatted using the dialect's placeholder syntax.
// Query hooks are invoked before and after execution.
func (db *DB) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row {
formattedQuery := db.format(query, args)
ctx, event := db.beforeQuery(ctx, nil, query, args, formattedQuery, nil)
@@ -355,11 +404,14 @@ 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
}
// Conn returns a Conn wrapping a dedicated *sql.Conn from the connection pool.
// Query hooks and dialect features remain active on the returned connection.
func (db *DB) Conn(ctx context.Context) (Conn, error) {
conn, err := db.DB.Conn(ctx)
if err != nil {
@@ -371,6 +423,7 @@ func (db *DB) Conn(ctx context.Context) (Conn, error) {
}, nil
}
// ExecContext executes a query without returning rows on this connection.
func (c Conn) ExecContext(
ctx context.Context, query string, args ...any,
) (sql.Result, error) {
@@ -381,6 +434,7 @@ func (c Conn) ExecContext(
return res, err
}
// QueryContext executes a query returning rows on this connection.
func (c Conn) QueryContext(
ctx context.Context, query string, args ...any,
) (*sql.Rows, error) {
@@ -391,6 +445,7 @@ func (c Conn) QueryContext(
return rows, err
}
// QueryRowContext executes a query expected to return at most one row on this connection.
func (c Conn) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row {
formattedQuery := c.db.format(query, args)
ctx, event := c.db.beforeQuery(ctx, nil, query, args, formattedQuery, nil)
@@ -399,62 +454,77 @@ func (c Conn) QueryRowContext(ctx context.Context, query string, args ...any) *s
return row
}
// Dialect returns the database dialect for this connection.
func (c Conn) Dialect() schema.Dialect {
return c.db.Dialect()
}
// NewValues creates a VALUES query bound to this connection.
func (c Conn) NewValues(model any) *ValuesQuery {
return NewValuesQuery(c.db, model).Conn(c)
}
// NewMerge creates a MERGE query bound to this connection.
func (c Conn) NewMerge() *MergeQuery {
return NewMergeQuery(c.db).Conn(c)
}
// NewSelect creates a SELECT query bound to this connection.
func (c Conn) NewSelect() *SelectQuery {
return NewSelectQuery(c.db).Conn(c)
}
// NewInsert creates an INSERT query bound to this connection.
func (c Conn) NewInsert() *InsertQuery {
return NewInsertQuery(c.db).Conn(c)
}
// NewUpdate creates an UPDATE query bound to this connection.
func (c Conn) NewUpdate() *UpdateQuery {
return NewUpdateQuery(c.db).Conn(c)
}
// NewDelete creates a DELETE query bound to this connection.
func (c Conn) NewDelete() *DeleteQuery {
return NewDeleteQuery(c.db).Conn(c)
}
// NewRaw creates a raw SQL query bound to this connection.
func (c Conn) NewRaw(query string, args ...any) *RawQuery {
return NewRawQuery(c.db, query, args...).Conn(c)
}
// NewCreateTable creates a CREATE TABLE query bound to this connection.
func (c Conn) NewCreateTable() *CreateTableQuery {
return NewCreateTableQuery(c.db).Conn(c)
}
// NewDropTable creates a DROP TABLE query bound to this connection.
func (c Conn) NewDropTable() *DropTableQuery {
return NewDropTableQuery(c.db).Conn(c)
}
// NewCreateIndex creates a CREATE INDEX query bound to this connection.
func (c Conn) NewCreateIndex() *CreateIndexQuery {
return NewCreateIndexQuery(c.db).Conn(c)
}
// NewDropIndex creates a DROP INDEX query bound to this connection.
func (c Conn) NewDropIndex() *DropIndexQuery {
return NewDropIndexQuery(c.db).Conn(c)
}
// NewTruncateTable creates a TRUNCATE TABLE query bound to this connection.
func (c Conn) NewTruncateTable() *TruncateTableQuery {
return NewTruncateTableQuery(c.db).Conn(c)
}
// NewAddColumn creates an ALTER TABLE ADD COLUMN query bound to this connection.
func (c Conn) NewAddColumn() *AddColumnQuery {
return NewAddColumnQuery(c.db).Conn(c)
}
// NewDropColumn creates an ALTER TABLE DROP COLUMN query bound to this connection.
func (c Conn) NewDropColumn() *DropColumnQuery {
return NewDropColumnQuery(c.db).Conn(c)
}
@@ -485,6 +555,7 @@ func (c Conn) RunInTx(
return tx.Commit()
}
// BeginTx starts a transaction on this connection with the given options.
func (c Conn) BeginTx(ctx context.Context, opts *sql.TxOptions) (Tx, error) {
ctx, event := c.db.beforeQuery(ctx, nil, "BEGIN", nil, "BEGIN", nil)
tx, err := c.Conn.BeginTx(ctx, opts)
@@ -501,14 +572,17 @@ 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
}
// Prepare creates a prepared statement using a background context.
func (db *DB) Prepare(query string) (Stmt, error) {
return db.PrepareContext(context.Background(), query)
}
// PrepareContext creates a prepared statement for repeated execution.
func (db *DB) PrepareContext(ctx context.Context, query string) (Stmt, error) {
stmt, err := db.DB.PrepareContext(ctx, query)
if err != nil {
@@ -519,6 +593,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
@@ -553,10 +628,12 @@ func (db *DB) RunInTx(
return tx.Commit()
}
// Begin starts a transaction with default options using a background context.
func (db *DB) Begin() (Tx, error) {
return db.BeginTx(context.Background(), nil)
}
// BeginTx starts a transaction with the given options.
func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (Tx, error) {
ctx, event := db.beforeQuery(ctx, nil, "BEGIN", nil, "BEGIN", nil)
tx, err := db.DB.BeginTx(ctx, opts)
@@ -571,6 +648,7 @@ func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (Tx, error) {
}, nil
}
// Commit commits the transaction or releases the savepoint if this is a nested transaction.
func (tx Tx) Commit() error {
if tx.name == "" {
return tx.commitTX()
@@ -594,6 +672,7 @@ func (tx Tx) commitSP() error {
return err
}
// Rollback rolls back the transaction or rolls back to the savepoint if this is a nested transaction.
func (tx Tx) Rollback() error {
if tx.name == "" {
return tx.rollbackTX()
@@ -617,10 +696,12 @@ func (tx Tx) rollbackSP() error {
return err
}
// Exec executes a query without returning rows within this transaction.
func (tx Tx) Exec(query string, args ...any) (sql.Result, error) {
return tx.ExecContext(context.TODO(), query, args...)
}
// ExecContext executes a query without returning rows within this transaction.
func (tx Tx) ExecContext(
ctx context.Context, query string, args ...any,
) (sql.Result, error) {
@@ -631,10 +712,12 @@ func (tx Tx) ExecContext(
return res, err
}
// Query executes a query returning rows within this transaction.
func (tx Tx) Query(query string, args ...any) (*sql.Rows, error) {
return tx.QueryContext(context.TODO(), query, args...)
}
// QueryContext executes a query returning rows within this transaction.
func (tx Tx) QueryContext(
ctx context.Context, query string, args ...any,
) (*sql.Rows, error) {
@@ -645,10 +728,12 @@ func (tx Tx) QueryContext(
return rows, err
}
// QueryRow executes a query expected to return at most one row within this transaction.
func (tx Tx) QueryRow(query string, args ...any) *sql.Row {
return tx.QueryRowContext(context.TODO(), query, args...)
}
// QueryRowContext executes a query expected to return at most one row within this transaction.
func (tx Tx) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row {
formattedQuery := tx.db.format(query, args)
ctx, event := tx.db.beforeQuery(ctx, nil, query, args, formattedQuery, nil)
@@ -659,6 +744,7 @@ func (tx Tx) QueryRowContext(ctx context.Context, query string, args ...any) *sq
//------------------------------------------------------------------------------
// Begin creates a savepoint, effectively starting a nested transaction.
func (tx Tx) Begin() (Tx, error) {
return tx.BeginTx(tx.ctx, nil)
}
@@ -689,6 +775,8 @@ func (tx Tx) BeginTx(ctx context.Context, _ *sql.TxOptions) (Tx, error) {
}, nil
}
// RunInTx creates a savepoint and runs the function within that savepoint.
// If the function returns an error, the savepoint is rolled back.
func (tx Tx) RunInTx(
ctx context.Context, _ *sql.TxOptions, fn func(ctx context.Context, tx Tx) error,
) error {
@@ -713,62 +801,77 @@ func (tx Tx) RunInTx(
return sp.Commit()
}
// Dialect returns the database dialect for this transaction.
func (tx Tx) Dialect() schema.Dialect {
return tx.db.Dialect()
}
// NewValues creates a VALUES query bound to this transaction.
func (tx Tx) NewValues(model any) *ValuesQuery {
return NewValuesQuery(tx.db, model).Conn(tx)
}
// NewMerge creates a MERGE query bound to this transaction.
func (tx Tx) NewMerge() *MergeQuery {
return NewMergeQuery(tx.db).Conn(tx)
}
// NewSelect creates a SELECT query bound to this transaction.
func (tx Tx) NewSelect() *SelectQuery {
return NewSelectQuery(tx.db).Conn(tx)
}
// NewInsert creates an INSERT query bound to this transaction.
func (tx Tx) NewInsert() *InsertQuery {
return NewInsertQuery(tx.db).Conn(tx)
}
// NewUpdate creates an UPDATE query bound to this transaction.
func (tx Tx) NewUpdate() *UpdateQuery {
return NewUpdateQuery(tx.db).Conn(tx)
}
// NewDelete creates a DELETE query bound to this transaction.
func (tx Tx) NewDelete() *DeleteQuery {
return NewDeleteQuery(tx.db).Conn(tx)
}
// NewRaw creates a raw SQL query bound to this transaction.
func (tx Tx) NewRaw(query string, args ...any) *RawQuery {
return NewRawQuery(tx.db, query, args...).Conn(tx)
}
// NewCreateTable creates a CREATE TABLE query bound to this transaction.
func (tx Tx) NewCreateTable() *CreateTableQuery {
return NewCreateTableQuery(tx.db).Conn(tx)
}
// NewDropTable creates a DROP TABLE query bound to this transaction.
func (tx Tx) NewDropTable() *DropTableQuery {
return NewDropTableQuery(tx.db).Conn(tx)
}
// NewCreateIndex creates a CREATE INDEX query bound to this transaction.
func (tx Tx) NewCreateIndex() *CreateIndexQuery {
return NewCreateIndexQuery(tx.db).Conn(tx)
}
// NewDropIndex creates a DROP INDEX query bound to this transaction.
func (tx Tx) NewDropIndex() *DropIndexQuery {
return NewDropIndexQuery(tx.db).Conn(tx)
}
// NewTruncateTable creates a TRUNCATE TABLE query bound to this transaction.
func (tx Tx) NewTruncateTable() *TruncateTableQuery {
return NewTruncateTableQuery(tx.db).Conn(tx)
}
// NewAddColumn creates an ALTER TABLE ADD COLUMN query bound to this transaction.
func (tx Tx) NewAddColumn() *AddColumnQuery {
return NewAddColumnQuery(tx.db).Conn(tx)
}
// NewDropColumn creates an ALTER TABLE DROP COLUMN query bound to this transaction.
func (tx Tx) NewDropColumn() *DropColumnQuery {
return NewDropColumnQuery(tx.db).Conn(tx)
}
+139 -50
View File
@@ -1,3 +1,6 @@
// Package feature defines flags that represent optional SQL dialect capabilities.
// Each dialect (PostgreSQL, MySQL, SQLite, MSSQL) declares which features it supports
// by combining flags with the bitwise OR operator.
package feature
import (
@@ -7,44 +10,110 @@ import (
"github.com/uptrace/bun/internal"
)
// Feature is a bit flag representing an optional SQL dialect capability.
type Feature = internal.Flag
const (
// Common query features.
// CTE enables Common Table Expressions (WITH ... AS ...) syntax.
CTE Feature = 1 << iota
// WithValues enables WITH ... (VALUES ...) syntax for inline value lists.
WithValues
// Returning enables the RETURNING clause to return rows affected by DML statements.
Returning
InsertReturning
Output // mssql
// Output enables the OUTPUT clause, the MSSQL equivalent of RETURNING.
Output
// DefaultPlaceholder enables the DEFAULT keyword as a placeholder in INSERT value lists.
DefaultPlaceholder
// DoubleColonCast enables PostgreSQL-style :: type cast syntax.
DoubleColonCast
// ValuesRow enables VALUES ROW(...) syntax.
ValuesRow
UpdateMultiTable
InsertTableAlias
UpdateTableAlias
DeleteTableAlias
AutoIncrement
Identity
TableCascade
TableIdentity
TableTruncate
InsertOnConflict // INSERT ... ON CONFLICT
InsertOnDuplicateKey // INSERT ... ON DUPLICATE KEY
InsertIgnore // INSERT IGNORE ...
TableNotExists
// CompositeIn enables WHERE (A, B) IN ((N, NN), (N, NN), ...) composite comparison syntax.
CompositeIn
// SELECT features.
// OffsetFetch enables OFFSET ... FETCH NEXT syntax (MSSQL).
OffsetFetch
// SelectExists enables EXISTS subquery expressions.
SelectExists
// INSERT features.
// InsertReturning enables INSERT ... RETURNING syntax.
InsertReturning
// InsertTableAlias enables table alias support in INSERT statements.
InsertTableAlias
// InsertOnConflict enables INSERT ... ON CONFLICT syntax (PostgreSQL, SQLite).
InsertOnConflict
// InsertOnDuplicateKey enables INSERT ... ON DUPLICATE KEY syntax (MySQL).
InsertOnDuplicateKey
// InsertIgnore enables INSERT IGNORE syntax to silently skip conflicting rows (MySQL).
InsertIgnore
// UPDATE features.
// UpdateFromTable enables UPDATE ... FROM ... syntax for joining tables in updates.
UpdateFromTable
MSSavepoint
GeneratedIdentity
CompositeIn // ... WHERE (A,B) IN ((N, NN), (N, NN)...)
UpdateOrderLimit // UPDATE ... ORDER BY ... LIMIT ...
DeleteOrderLimit // DELETE ... ORDER BY ... LIMIT ...
// UpdateMultiTable enables multi-table UPDATE syntax (MySQL).
UpdateMultiTable
// UpdateTableAlias enables table alias support in UPDATE statements.
UpdateTableAlias
// UpdateOrderLimit enables UPDATE ... ORDER BY ... LIMIT syntax.
UpdateOrderLimit
// DELETE features.
// DeleteReturning enables DELETE ... RETURNING syntax.
DeleteReturning
// DeleteTableAlias enables table alias support in DELETE statements.
DeleteTableAlias
// DeleteOrderLimit enables DELETE ... ORDER BY ... LIMIT syntax.
DeleteOrderLimit
// MERGE features.
// Merge enables MERGE ... USING ... ON ... WHEN syntax for upsert operations.
Merge
// MergeReturning enables MERGE ... RETURNING syntax.
MergeReturning
AlterColumnExists // ADD/DROP COLUMN IF NOT EXISTS/IF EXISTS
FKDefaultOnAction // FK ON UPDATE/ON DELETE has default value: NO ACTION
// Table DDL features.
// TableCascade enables CASCADE support for DROP TABLE and related operations.
TableCascade
// TableIdentity enables table-level IDENTITY property (MSSQL).
TableIdentity
// TableTruncate enables TRUNCATE TABLE support.
TableTruncate
// TableNotExists enables IF NOT EXISTS / IF EXISTS syntax for CREATE TABLE and DROP TABLE.
TableNotExists
// AlterColumnExists enables ADD/DROP COLUMN IF NOT EXISTS / IF EXISTS syntax.
AlterColumnExists
// CreateIndexIfNotExists enables CREATE INDEX IF NOT EXISTS syntax.
CreateIndexIfNotExists
// Column definition features.
// AutoIncrement enables AUTO_INCREMENT syntax for auto-generated columns (MySQL).
AutoIncrement
// Identity enables IDENTITY column syntax for auto-generated columns (MSSQL).
Identity
// GeneratedIdentity enables GENERATED ALWAYS AS IDENTITY syntax (PostgreSQL).
GeneratedIdentity
// Dialect-specific features.
// FKDefaultOnAction indicates that FK ON UPDATE/ON DELETE has the default value NO ACTION.
FKDefaultOnAction
// MSSavepoint enables Microsoft SQL Server savepoint support.
MSSavepoint
)
// NotSupportError is returned when an operation requires a feature
// that the current dialect does not support.
type NotSupportError struct {
Flag Feature
}
@@ -57,42 +126,62 @@ func (err *NotSupportError) Error() string {
return fmt.Sprintf("bun: feature %s is not supported by current dialect", name)
}
// NewNotSupportError returns a NotSupportError for the given feature flag.
func NewNotSupportError(flag Feature) *NotSupportError {
return &NotSupportError{Flag: flag}
}
var flag2str = map[Feature]string{
CTE: "CTE",
WithValues: "WithValues",
Returning: "Returning",
// Common query features.
CTE: "CTE",
WithValues: "WithValues",
Returning: "Returning",
Output: "Output",
DefaultPlaceholder: "DefaultPlaceholder",
DoubleColonCast: "DoubleColonCast",
ValuesRow: "ValuesRow",
CompositeIn: "CompositeIn",
// SELECT features.
OffsetFetch: "OffsetFetch",
SelectExists: "SelectExists",
// INSERT features.
InsertReturning: "InsertReturning",
Output: "Output",
DefaultPlaceholder: "DefaultPlaceholder",
DoubleColonCast: "DoubleColonCast",
ValuesRow: "ValuesRow",
UpdateMultiTable: "UpdateMultiTable",
InsertTableAlias: "InsertTableAlias",
UpdateTableAlias: "UpdateTableAlias",
DeleteTableAlias: "DeleteTableAlias",
AutoIncrement: "AutoIncrement",
Identity: "Identity",
TableCascade: "TableCascade",
TableIdentity: "TableIdentity",
TableTruncate: "TableTruncate",
InsertOnConflict: "InsertOnConflict",
InsertOnDuplicateKey: "InsertOnDuplicateKey",
InsertIgnore: "InsertIgnore",
TableNotExists: "TableNotExists",
OffsetFetch: "OffsetFetch",
SelectExists: "SelectExists",
UpdateFromTable: "UpdateFromTable",
MSSavepoint: "MSSavepoint",
GeneratedIdentity: "GeneratedIdentity",
CompositeIn: "CompositeIn",
UpdateOrderLimit: "UpdateOrderLimit",
DeleteOrderLimit: "DeleteOrderLimit",
DeleteReturning: "DeleteReturning",
MergeReturning: "MergeReturning",
AlterColumnExists: "AlterColumnExists",
FKDefaultOnAction: "FKDefaultOnAction",
// UPDATE features.
UpdateFromTable: "UpdateFromTable",
UpdateMultiTable: "UpdateMultiTable",
UpdateTableAlias: "UpdateTableAlias",
UpdateOrderLimit: "UpdateOrderLimit",
// DELETE features.
DeleteReturning: "DeleteReturning",
DeleteTableAlias: "DeleteTableAlias",
DeleteOrderLimit: "DeleteOrderLimit",
// MERGE features.
Merge: "Merge",
MergeReturning: "MergeReturning",
// Table DDL features.
TableCascade: "TableCascade",
TableIdentity: "TableIdentity",
TableTruncate: "TableTruncate",
TableNotExists: "TableNotExists",
AlterColumnExists: "AlterColumnExists",
CreateIndexIfNotExists: "CreateIndexIfNotExists",
// Column definition features.
AutoIncrement: "AutoIncrement",
Identity: "Identity",
GeneratedIdentity: "GeneratedIdentity",
// Dialect-specific features.
FKDefaultOnAction: "FKDefaultOnAction",
MSSavepoint: "MSSavepoint",
}
+1
View File
@@ -7,6 +7,7 @@ import (
var _ Provider = (*StdProvider)(nil)
// StdProvider implements the JSON Provider interface using the standard encoding/json package.
type StdProvider struct{}
func (StdProvider) Marshal(v any) ([]byte, error) {
+3
View File
@@ -9,6 +9,7 @@ import (
"unicode"
)
// QueryEvent captures information about a query execution for hooks.
type QueryEvent struct {
DB *DB
@@ -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()
@@ -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)
+27 -2
View File
@@ -115,8 +115,10 @@ func (p *Parser) ReadIdentifier() (string, bool) {
s := p.i + 1
if ind := bytes.IndexByte(p.b[s:], ')'); ind != -1 {
b := p.b[s : s+ind]
p.i = s + ind + 1
return internal.String(b), false
if isIdent(b) {
p.i = s + ind + 1
return internal.String(b), false
}
}
}
@@ -167,3 +169,26 @@ func isNum(c byte) bool {
func isAlpha(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
// isIdent reports whether b is a valid identifier consisting of
// letters, digits, and underscores, with at least one letter.
func isIdent(b []byte) bool {
if len(b) == 0 {
return false
}
hasAlpha := false
for i, c := range b {
if isAlpha(c) {
hasAlpha = true
continue
}
if isNum(c) {
continue
}
if c == '_' && i > 0 {
continue
}
return false
}
return hasAlpha
}
+2
View File
@@ -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
+1 -1
View File
@@ -168,7 +168,7 @@ func indirectAsKey(field reflect.Value) any {
i := field.Interface()
if valuer, ok := i.(driver.Valuer); ok {
if v, err := valuer.Value(); err == nil {
if v, err := valuer.Value(); err == nil && v != nil {
switch reflect.TypeOf(v).Kind() {
case reflect.Array, reflect.Chan, reflect.Func,
reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer:
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gobun",
"version": "1.2.16",
"version": "1.2.18",
"main": "index.js",
"repository": "git@github.com:uptrace/bun.git",
"author": "Vladimir Mihailenco <vladimir.webdev@gmail.com>",
+10 -1
View File
@@ -22,6 +22,7 @@ const (
allWithDeletedFlag
)
// WithQuery defines a common table expression used by another query.
type WithQuery struct {
name string
query Query
@@ -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
@@ -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,
@@ -264,16 +266,19 @@ func NewWithQuery(name string, query Query) *WithQuery {
}
}
// Recursive marks the CTE as recursive, enabling self-referential queries.
func (q *WithQuery) Recursive() *WithQuery {
q.recursive = true
return q
}
// Materialized hints the planner to evaluate the CTE once and store the results.
func (q *WithQuery) Materialized() *WithQuery {
q.materialized = true
return q
}
// NotMaterialized hints the planner to inline the CTE, potentially re-evaluating it.
func (q *WithQuery) NotMaterialized() *WithQuery {
q.notMaterialized = true
return q
@@ -1160,6 +1165,7 @@ func (q *setQuery) appendSet(gen schema.QueryGen, b []byte) (_ []byte, err error
func (q *setQuery) appendSetStruct(
gen schema.QueryGen, b []byte, model *structTableModel, fields []*schema.Field,
) (_ []byte, err error) {
defaultPlaceholder := gen.HasFeature(feature.DefaultPlaceholder)
isTemplate := gen.IsNop()
pos := len(b)
for _, f := range fields {
@@ -1191,6 +1197,8 @@ func (q *setQuery) appendSetStruct(
if err != nil {
return nil, err
}
} else if defaultPlaceholder {
b = f.AppendValueOrDefault(gen, b, model.strct)
} else {
b = f.AppendValue(gen, b, model.strct)
}
@@ -1568,6 +1576,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 {
+2
View File
@@ -10,6 +10,7 @@ import (
"github.com/uptrace/bun/schema"
)
// AddColumnQuery builds ALTER TABLE ... ADD COLUMN statements.
type AddColumnQuery struct {
baseQuery
@@ -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{
+2
View File
@@ -9,6 +9,7 @@ import (
"github.com/uptrace/bun/schema"
)
// DropColumnQuery builds ALTER TABLE ... DROP COLUMN statements.
type DropColumnQuery struct {
baseQuery
@@ -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{
+2
View File
@@ -11,6 +11,7 @@ import (
"github.com/uptrace/bun/schema"
)
// DeleteQuery builds SQL DELETE statements.
type DeleteQuery struct {
whereBaseQuery
orderLimitOffsetQuery
@@ -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{
+4 -1
View File
@@ -4,10 +4,12 @@ import (
"context"
"database/sql"
"github.com/uptrace/bun/dialect/feature"
"github.com/uptrace/bun/internal"
"github.com/uptrace/bun/schema"
)
// CreateIndexQuery builds CREATE INDEX statements.
type CreateIndexQuery struct {
whereBaseQuery
@@ -25,6 +27,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{
@@ -185,7 +188,7 @@ func (q *CreateIndexQuery) AppendQuery(gen schema.QueryGen, b []byte) (_ []byte,
if q.concurrently {
b = append(b, "CONCURRENTLY "...)
}
if q.ifNotExists {
if q.ifNotExists && gen.HasFeature(feature.CreateIndexIfNotExists) {
b = append(b, "IF NOT EXISTS "...)
}
+2
View File
@@ -8,6 +8,7 @@ import (
"github.com/uptrace/bun/schema"
)
// DropIndexQuery builds DROP INDEX statements.
type DropIndexQuery struct {
baseQuery
cascadeQuery
@@ -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{
+2
View File
@@ -12,6 +12,7 @@ import (
"github.com/uptrace/bun/schema"
)
// InsertQuery builds SQL INSERT statements.
type InsertQuery struct {
whereBaseQuery
returningQuery
@@ -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{
+3 -1
View File
@@ -11,6 +11,7 @@ import (
"github.com/uptrace/bun/schema"
)
// MergeQuery builds MERGE statements for dialects that support them.
type MergeQuery struct {
baseQuery
returningQuery
@@ -23,13 +24,14 @@ 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{
db: db,
},
}
if q.db.dialect.Name() != dialect.MSSQL && q.db.dialect.Name() != dialect.PG {
if !q.db.HasFeature(feature.Merge) {
q.setErr(errors.New("bun: merge not supported for current dialect"))
}
return q
+2
View File
@@ -7,6 +7,7 @@ import (
"github.com/uptrace/bun/schema"
)
// RawQuery executes a plain SQL query using Bun formatting and hooks.
type RawQuery struct {
baseQuery
@@ -15,6 +16,7 @@ type RawQuery struct {
comment string
}
// NewRawQuery creates a RawQuery with the provided SQL template and arguments.
func NewRawQuery(db *DB, query string, args ...any) *RawQuery {
return &RawQuery{
baseQuery: baseQuery{
+63
View File
@@ -20,6 +20,7 @@ type union struct {
query *SelectQuery
}
// SelectQuery builds SQL SELECT statements.
type SelectQuery struct {
whereBaseQuery
idxHintsQuery
@@ -37,6 +38,7 @@ type SelectQuery struct {
var _ Query = (*SelectQuery)(nil)
// NewSelectQuery returns a SelectQuery attached to the provided DB.
func NewSelectQuery(db *DB) *SelectQuery {
return &SelectQuery{
whereBaseQuery: whereBaseQuery{
@@ -47,16 +49,19 @@ func NewSelectQuery(db *DB) *SelectQuery {
}
}
// Conn sets the database connection for this query.
func (q *SelectQuery) Conn(db IConn) *SelectQuery {
q.setConn(db)
return q
}
// Model sets the model to select into and generates SELECT and FROM clauses.
func (q *SelectQuery) Model(model any) *SelectQuery {
q.setModel(model)
return q
}
// Err sets an error on the query, causing subsequent operations to fail.
func (q *SelectQuery) Err(err error) *SelectQuery {
q.setErr(err)
return q
@@ -72,26 +77,31 @@ func (q *SelectQuery) Apply(fns ...func(*SelectQuery) *SelectQuery) *SelectQuery
return q
}
// With adds a WITH clause (Common Table Expression) to the query.
func (q *SelectQuery) With(name string, query Query) *SelectQuery {
q.addWith(NewWithQuery(name, query))
return q
}
// WithRecursive adds a WITH RECURSIVE clause to the query.
func (q *SelectQuery) WithRecursive(name string, query Query) *SelectQuery {
q.addWith(NewWithQuery(name, query).Recursive())
return q
}
// WithQuery adds a pre-configured WITH clause to the query.
func (q *SelectQuery) WithQuery(query *WithQuery) *SelectQuery {
q.addWith(query)
return q
}
// Distinct adds a DISTINCT clause to eliminate duplicate rows.
func (q *SelectQuery) Distinct() *SelectQuery {
q.distinctOn = make([]schema.QueryWithArgs, 0)
return q
}
// DistinctOn adds a DISTINCT ON clause for PostgreSQL-specific distinct behavior.
func (q *SelectQuery) DistinctOn(query string, args ...any) *SelectQuery {
q.distinctOn = append(q.distinctOn, schema.SafeQuery(query, args))
return q
@@ -99,6 +109,7 @@ func (q *SelectQuery) DistinctOn(query string, args ...any) *SelectQuery {
//------------------------------------------------------------------------------
// Table specifies the table(s) to select from.
func (q *SelectQuery) Table(tables ...string) *SelectQuery {
for _, table := range tables {
q.addTable(schema.UnsafeIdent(table))
@@ -106,11 +117,13 @@ func (q *SelectQuery) Table(tables ...string) *SelectQuery {
return q
}
// TableExpr adds a table expression to the FROM clause with arguments.
func (q *SelectQuery) TableExpr(query string, args ...any) *SelectQuery {
q.addTable(schema.SafeQuery(query, args))
return q
}
// ModelTableExpr overrides the table name derived from the model.
func (q *SelectQuery) ModelTableExpr(query string, args ...any) *SelectQuery {
q.modelTableName = schema.SafeQuery(query, args)
return q
@@ -118,6 +131,7 @@ func (q *SelectQuery) ModelTableExpr(query string, args ...any) *SelectQuery {
//------------------------------------------------------------------------------
// Column adds columns to the SELECT clause.
func (q *SelectQuery) Column(columns ...string) *SelectQuery {
for _, column := range columns {
q.addColumn(schema.UnsafeIdent(column))
@@ -125,11 +139,13 @@ func (q *SelectQuery) Column(columns ...string) *SelectQuery {
return q
}
// ColumnExpr adds a column expression to the SELECT clause with arguments.
func (q *SelectQuery) ColumnExpr(query string, args ...any) *SelectQuery {
q.addColumn(schema.SafeQuery(query, args))
return q
}
// ExcludeColumn excludes specific columns from being selected.
func (q *SelectQuery) ExcludeColumn(columns ...string) *SelectQuery {
q.excludeColumn(columns)
return q
@@ -137,21 +153,25 @@ func (q *SelectQuery) ExcludeColumn(columns ...string) *SelectQuery {
//------------------------------------------------------------------------------
// WherePK adds a WHERE condition on the model's primary key columns.
func (q *SelectQuery) WherePK(cols ...string) *SelectQuery {
q.addWhereCols(cols)
return q
}
// Where adds a WHERE condition combined with AND.
func (q *SelectQuery) Where(query string, args ...any) *SelectQuery {
q.addWhere(schema.SafeQueryWithSep(query, args, " AND "))
return q
}
// WhereOr adds a WHERE condition combined with OR.
func (q *SelectQuery) WhereOr(query string, args ...any) *SelectQuery {
q.addWhere(schema.SafeQueryWithSep(query, args, " OR "))
return q
}
// WhereGroup groups WHERE conditions with the given separator (AND/OR).
func (q *SelectQuery) WhereGroup(sep string, fn func(*SelectQuery) *SelectQuery) *SelectQuery {
saved := q.where
q.where = nil
@@ -166,11 +186,13 @@ func (q *SelectQuery) WhereGroup(sep string, fn func(*SelectQuery) *SelectQuery)
return q
}
// WhereDeleted adds a WHERE condition to select soft-deleted rows only.
func (q *SelectQuery) WhereDeleted() *SelectQuery {
q.whereDeleted()
return q
}
// WhereAllWithDeleted includes both active and soft-deleted rows.
func (q *SelectQuery) WhereAllWithDeleted() *SelectQuery {
q.whereAllWithDeleted()
return q
@@ -178,6 +200,7 @@ func (q *SelectQuery) WhereAllWithDeleted() *SelectQuery {
//------------------------------------------------------------------------------
// UseIndex adds a USE INDEX hint for MySQL to suggest index usage.
func (q *SelectQuery) UseIndex(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addUseIndex(indexes...)
@@ -185,6 +208,7 @@ func (q *SelectQuery) UseIndex(indexes ...string) *SelectQuery {
return q
}
// UseIndexForJoin adds a USE INDEX FOR JOIN hint for MySQL.
func (q *SelectQuery) UseIndexForJoin(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addUseIndexForJoin(indexes...)
@@ -192,6 +216,7 @@ func (q *SelectQuery) UseIndexForJoin(indexes ...string) *SelectQuery {
return q
}
// UseIndexForOrderBy adds a USE INDEX FOR ORDER BY hint for MySQL.
func (q *SelectQuery) UseIndexForOrderBy(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addUseIndexForOrderBy(indexes...)
@@ -199,6 +224,7 @@ func (q *SelectQuery) UseIndexForOrderBy(indexes ...string) *SelectQuery {
return q
}
// UseIndexForGroupBy adds a USE INDEX FOR GROUP BY hint for MySQL.
func (q *SelectQuery) UseIndexForGroupBy(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addUseIndexForGroupBy(indexes...)
@@ -206,6 +232,7 @@ func (q *SelectQuery) UseIndexForGroupBy(indexes ...string) *SelectQuery {
return q
}
// IgnoreIndex adds an IGNORE INDEX hint for MySQL to prevent index usage.
func (q *SelectQuery) IgnoreIndex(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addIgnoreIndex(indexes...)
@@ -213,6 +240,7 @@ func (q *SelectQuery) IgnoreIndex(indexes ...string) *SelectQuery {
return q
}
// IgnoreIndexForJoin adds an IGNORE INDEX FOR JOIN hint for MySQL.
func (q *SelectQuery) IgnoreIndexForJoin(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addIgnoreIndexForJoin(indexes...)
@@ -220,6 +248,7 @@ func (q *SelectQuery) IgnoreIndexForJoin(indexes ...string) *SelectQuery {
return q
}
// IgnoreIndexForOrderBy adds an IGNORE INDEX FOR ORDER BY hint for MySQL.
func (q *SelectQuery) IgnoreIndexForOrderBy(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addIgnoreIndexForOrderBy(indexes...)
@@ -227,6 +256,7 @@ func (q *SelectQuery) IgnoreIndexForOrderBy(indexes ...string) *SelectQuery {
return q
}
// IgnoreIndexForGroupBy adds an IGNORE INDEX FOR GROUP BY hint for MySQL.
func (q *SelectQuery) IgnoreIndexForGroupBy(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addIgnoreIndexForGroupBy(indexes...)
@@ -234,6 +264,7 @@ func (q *SelectQuery) IgnoreIndexForGroupBy(indexes ...string) *SelectQuery {
return q
}
// ForceIndex adds a FORCE INDEX hint for MySQL to require index usage.
func (q *SelectQuery) ForceIndex(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addForceIndex(indexes...)
@@ -241,6 +272,7 @@ func (q *SelectQuery) ForceIndex(indexes ...string) *SelectQuery {
return q
}
// ForceIndexForJoin adds a FORCE INDEX FOR JOIN hint for MySQL.
func (q *SelectQuery) ForceIndexForJoin(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addForceIndexForJoin(indexes...)
@@ -248,6 +280,7 @@ func (q *SelectQuery) ForceIndexForJoin(indexes ...string) *SelectQuery {
return q
}
// ForceIndexForOrderBy adds a FORCE INDEX FOR ORDER BY hint for MySQL.
func (q *SelectQuery) ForceIndexForOrderBy(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addForceIndexForOrderBy(indexes...)
@@ -255,6 +288,7 @@ func (q *SelectQuery) ForceIndexForOrderBy(indexes ...string) *SelectQuery {
return q
}
// ForceIndexForGroupBy adds a FORCE INDEX FOR GROUP BY hint for MySQL.
func (q *SelectQuery) ForceIndexForGroupBy(indexes ...string) *SelectQuery {
if q.db.dialect.Name() == dialect.MySQL {
q.addForceIndexForGroupBy(indexes...)
@@ -264,6 +298,7 @@ func (q *SelectQuery) ForceIndexForGroupBy(indexes ...string) *SelectQuery {
//------------------------------------------------------------------------------
// Group adds columns to the GROUP BY clause.
func (q *SelectQuery) Group(columns ...string) *SelectQuery {
for _, column := range columns {
q.group = append(q.group, schema.UnsafeIdent(column))
@@ -271,41 +306,49 @@ func (q *SelectQuery) Group(columns ...string) *SelectQuery {
return q
}
// GroupExpr adds a GROUP BY expression with optional arguments.
func (q *SelectQuery) GroupExpr(group string, args ...any) *SelectQuery {
q.group = append(q.group, schema.SafeQuery(group, args))
return q
}
// Having adds a HAVING clause condition to filter grouped results.
func (q *SelectQuery) Having(having string, args ...any) *SelectQuery {
q.having = append(q.having, schema.SafeQuery(having, args))
return q
}
// Order adds columns to the ORDER BY clause.
func (q *SelectQuery) Order(orders ...string) *SelectQuery {
q.addOrder(orders...)
return q
}
// OrderBy adds an ORDER BY clause with explicit sort direction.
func (q *SelectQuery) OrderBy(colName string, sortDir Order) *SelectQuery {
q.addOrderBy(colName, sortDir)
return q
}
// OrderExpr adds an ORDER BY expression with optional arguments.
func (q *SelectQuery) OrderExpr(query string, args ...any) *SelectQuery {
q.addOrderExpr(query, args...)
return q
}
// Limit sets the maximum number of rows to return.
func (q *SelectQuery) Limit(n int) *SelectQuery {
q.setLimit(n)
return q
}
// Offset sets the number of rows to skip before returning results.
func (q *SelectQuery) Offset(n int) *SelectQuery {
q.setOffset(n)
return q
}
// For adds a FOR clause for row locking (e.g., "UPDATE", "SHARE").
func (q *SelectQuery) For(s string, args ...any) *SelectQuery {
q.selFor = schema.SafeQuery(s, args)
return q
@@ -313,26 +356,32 @@ func (q *SelectQuery) For(s string, args ...any) *SelectQuery {
//------------------------------------------------------------------------------
// Union combines this query with another using UNION (removes duplicates).
func (q *SelectQuery) Union(other *SelectQuery) *SelectQuery {
return q.addUnion(" UNION ", other)
}
// UnionAll combines this query with another using UNION ALL (keeps duplicates).
func (q *SelectQuery) UnionAll(other *SelectQuery) *SelectQuery {
return q.addUnion(" UNION ALL ", other)
}
// Intersect returns rows that appear in both this query and another (removes duplicates).
func (q *SelectQuery) Intersect(other *SelectQuery) *SelectQuery {
return q.addUnion(" INTERSECT ", other)
}
// IntersectAll returns rows that appear in both this query and another (keeps duplicates).
func (q *SelectQuery) IntersectAll(other *SelectQuery) *SelectQuery {
return q.addUnion(" INTERSECT ALL ", other)
}
// Except returns rows in this query that are not in another (removes duplicates).
func (q *SelectQuery) Except(other *SelectQuery) *SelectQuery {
return q.addUnion(" EXCEPT ", other)
}
// ExceptAll returns rows in this query that are not in another (keeps duplicates).
func (q *SelectQuery) ExceptAll(other *SelectQuery) *SelectQuery {
return q.addUnion(" EXCEPT ALL ", other)
}
@@ -347,6 +396,7 @@ func (q *SelectQuery) addUnion(expr string, other *SelectQuery) *SelectQuery {
//------------------------------------------------------------------------------
// Join adds a JOIN clause with the specified join expression.
func (q *SelectQuery) Join(join string, args ...any) *SelectQuery {
q.joins = append(q.joins, joinQuery{
join: schema.SafeQuery(join, args),
@@ -354,10 +404,12 @@ func (q *SelectQuery) Join(join string, args ...any) *SelectQuery {
return q
}
// JoinOn adds an ON condition to the most recent JOIN, combined with AND.
func (q *SelectQuery) JoinOn(cond string, args ...any) *SelectQuery {
return q.joinOn(cond, args, " AND ")
}
// JoinOnOr adds an ON condition to the most recent JOIN, combined with OR.
func (q *SelectQuery) JoinOnOr(cond string, args ...any) *SelectQuery {
return q.joinOn(cond, args, " OR ")
}
@@ -396,6 +448,7 @@ func (q *SelectQuery) Relation(name string, apply ...func(*SelectQuery) *SelectQ
return q
}
// RelationOpts configures how a relation is joined in a SelectQuery.
type RelationOpts struct {
// Apply applies additional options to the relation.
Apply func(*SelectQuery) *SelectQuery
@@ -513,6 +566,7 @@ func (q *SelectQuery) Comment(comment string) *SelectQuery {
//------------------------------------------------------------------------------
// Operation returns the query operation name ("SELECT").
func (q *SelectQuery) Operation() string {
return "SELECT"
}
@@ -792,6 +846,7 @@ func (q *SelectQuery) appendTables(gen schema.QueryGen, b []byte) (_ []byte, err
//------------------------------------------------------------------------------
// Rows executes the query and returns the result rows for manual scanning.
func (q *SelectQuery) Rows(ctx context.Context) (*sql.Rows, error) {
if q.err != nil {
return nil, q.err
@@ -817,6 +872,7 @@ func (q *SelectQuery) Rows(ctx context.Context) (*sql.Rows, error) {
return rows, err
}
// Exec executes the query and optionally scans results into dest.
func (q *SelectQuery) Exec(ctx context.Context, dest ...any) (res sql.Result, err error) {
if q.err != nil {
return nil, q.err
@@ -855,6 +911,7 @@ func (q *SelectQuery) Exec(ctx context.Context, dest ...any) (res sql.Result, er
return res, nil
}
// Scan executes the query and scans the results into dest.
func (q *SelectQuery) Scan(ctx context.Context, dest ...any) error {
_, err := q.scanResult(ctx, dest...)
return err
@@ -938,6 +995,7 @@ func (q *SelectQuery) afterSelectHook(ctx context.Context) error {
return nil
}
// Count executes the query and returns the number of rows that match.
func (q *SelectQuery) Count(ctx context.Context) (int, error) {
if q.err != nil {
return 0, q.err
@@ -964,6 +1022,7 @@ func (q *SelectQuery) Count(ctx context.Context) (int, error) {
return num, err
}
// ScanAndCount executes the query, scans results into dest, and returns the total count.
func (q *SelectQuery) ScanAndCount(ctx context.Context, dest ...any) (int, error) {
if q.offset == 0 && q.limit == 0 {
// If there is no limit and offset, we can use a single query to get the count and scan
@@ -1044,6 +1103,7 @@ func (q *SelectQuery) scanAndCountSeq(ctx context.Context, dest ...any) (int, er
return count, firstErr
}
// Exists checks whether any rows match the query.
func (q *SelectQuery) Exists(ctx context.Context) (bool, error) {
if q.err != nil {
return false, q.err
@@ -1112,6 +1172,7 @@ func (q *SelectQuery) String() string {
return string(buf)
}
// Clone creates a deep copy of the SelectQuery.
func (q *SelectQuery) Clone() *SelectQuery {
if q == nil {
return nil
@@ -1232,10 +1293,12 @@ func (q *SelectQuery) Clone() *SelectQuery {
//------------------------------------------------------------------------------
// QueryBuilder wraps the SelectQuery in a generic QueryBuilder interface.
func (q *SelectQuery) QueryBuilder() QueryBuilder {
return &selectQueryBuilder{q}
}
// ApplyQueryBuilder applies a function to a generic QueryBuilder and returns the modified SelectQuery.
func (q *SelectQuery) ApplyQueryBuilder(fn func(QueryBuilder) QueryBuilder) *SelectQuery {
return fn(q.QueryBuilder()).Unwrap().(*SelectQuery)
}
+2
View File
@@ -16,6 +16,7 @@ import (
"github.com/uptrace/bun/schema"
)
// CreateTableQuery builds CREATE TABLE statements.
type CreateTableQuery struct {
baseQuery
@@ -37,6 +38,7 @@ type CreateTableQuery struct {
var _ Query = (*CreateTableQuery)(nil)
// NewCreateTableQuery returns a CreateTableQuery bound to the provided DB.
func NewCreateTableQuery(db *DB) *CreateTableQuery {
q := &CreateTableQuery{
baseQuery: baseQuery{
+2
View File
@@ -8,6 +8,7 @@ import (
"github.com/uptrace/bun/schema"
)
// DropTableQuery builds DROP TABLE statements.
type DropTableQuery struct {
baseQuery
cascadeQuery
@@ -18,6 +19,7 @@ type DropTableQuery struct {
var _ Query = (*DropTableQuery)(nil)
// NewDropTableQuery returns a DropTableQuery tied to the provided DB.
func NewDropTableQuery(db *DB) *DropTableQuery {
q := &DropTableQuery{
baseQuery: baseQuery{
+2
View File
@@ -9,6 +9,7 @@ import (
"github.com/uptrace/bun/schema"
)
// TruncateTableQuery builds TRUNCATE TABLE statements.
type TruncateTableQuery struct {
baseQuery
cascadeQuery
@@ -19,6 +20,7 @@ type TruncateTableQuery struct {
var _ Query = (*TruncateTableQuery)(nil)
// NewTruncateTableQuery creates a TruncateTableQuery attached to the given DB.
func NewTruncateTableQuery(db *DB) *TruncateTableQuery {
q := &TruncateTableQuery{
baseQuery: baseQuery{
+2
View File
@@ -13,6 +13,7 @@ import (
"github.com/uptrace/bun/schema"
)
// UpdateQuery builds SQL UPDATE statements.
type UpdateQuery struct {
whereBaseQuery
orderLimitOffsetQuery
@@ -26,6 +27,7 @@ type UpdateQuery struct {
var _ Query = (*UpdateQuery)(nil)
// NewUpdateQuery returns a new UpdateQuery attached to the provided DB.
func NewUpdateQuery(db *DB) *UpdateQuery {
q := &UpdateQuery{
whereBaseQuery: whereBaseQuery{
+2
View File
@@ -9,6 +9,7 @@ import (
"github.com/uptrace/bun/schema"
)
// ValuesQuery builds VALUES clauses that can be used as subqueries.
type ValuesQuery struct {
baseQuery
setQuery
@@ -22,6 +23,7 @@ var (
_ schema.NamedArgAppender = (*ValuesQuery)(nil)
)
// NewValuesQuery creates a VALUES query for the given model.
func NewValuesQuery(db *DB, model any) *ValuesQuery {
q := &ValuesQuery{
baseQuery: baseQuery{
+1
View File
@@ -7,6 +7,7 @@ import (
"github.com/uptrace/bun/dialect"
)
// Deprecated: Use List or Tuple instead.
func In(slice any) QueryAppender {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
+12 -1
View File
@@ -87,7 +87,7 @@ func (f *Field) HasZeroValue(v reflect.Value) bool {
}
for _, index := range f.Index {
if v.Kind() == reflect.Ptr {
if v.Kind() == reflect.Pointer {
if v.IsNil() {
return true
}
@@ -99,12 +99,23 @@ func (f *Field) HasZeroValue(v reflect.Value) bool {
}
func (f *Field) AppendValue(gen QueryGen, b []byte, strct reflect.Value) []byte {
return f.appendValue(gen, b, strct, false)
}
func (f *Field) AppendValueOrDefault(gen QueryGen, b []byte, strct reflect.Value) []byte {
return f.appendValue(gen, b, strct, true)
}
func (f *Field) appendValue(gen QueryGen, b []byte, strct reflect.Value, defaultPlaceholder bool) []byte {
fv, ok := fieldByIndex(strct, f.Index)
if !ok {
return dialect.AppendNull(b)
}
if (f.IsPtr && fv.IsNil()) || (f.NullZero && f.IsZero(fv)) {
if defaultPlaceholder {
return append(b, "DEFAULT"...)
}
return dialect.AppendNull(b)
}
if f.Append == nil {
+2
View File
@@ -4,6 +4,7 @@ import (
"fmt"
)
// Relation type constants define the possible types of table relationships.
const (
InvalidRelation = iota
HasOneRelation
@@ -12,6 +13,7 @@ const (
ManyToManyRelation
)
// Relation describes how two tables are related (has-one, belongs-to, has-many, or many-to-many).
type Relation struct {
Type int
Field *Field // Has the bun tag defining this relation.
+5
View File
@@ -7,10 +7,12 @@ import (
"github.com/uptrace/bun/internal"
)
// QueryAppender is implemented by types that can append themselves to a SQL query.
type QueryAppender interface {
AppendQuery(gen QueryGen, b []byte) ([]byte, error)
}
// ColumnsAppender is implemented by types that can append column definitions to a SQL query.
type ColumnsAppender interface {
AppendColumns(gen QueryGen, b []byte) ([]byte, error)
}
@@ -51,6 +53,7 @@ func (s Ident) AppendQuery(gen QueryGen, b []byte) ([]byte, error) {
//------------------------------------------------------------------------------
// QueryWithArgs is a query string paired with its arguments.
// NOTE: It should not be modified after creation.
type QueryWithArgs struct {
Query string
@@ -88,6 +91,7 @@ func (q QueryWithArgs) AppendQuery(gen QueryGen, b []byte) ([]byte, error) {
//------------------------------------------------------------------------------
// Order represents a SQL sort direction.
type Order string
const (
@@ -120,6 +124,7 @@ func AppendOrder(b []byte, sortDir Order) []byte {
//------------------------------------------------------------------------------
// QueryWithSep is a QueryWithArgs with an additional separator.
type QueryWithSep struct {
QueryWithArgs
Sep string
+1
View File
@@ -50,6 +50,7 @@ func isZero(v any) bool {
}
}
// IsZeroerFunc reports whether a reflect.Value is a zero value.
type IsZeroerFunc func(reflect.Value) bool
func zeroChecker(typ reflect.Type) IsZeroerFunc {
+1 -1
View File
@@ -2,5 +2,5 @@ package bun
// Version is the current release version.
func Version() string {
return "1.2.16"
return "1.2.18"
}