chore: ⬆️ updated deps
This commit is contained in:
+29
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -2,5 +2,5 @@ package bun
|
||||
|
||||
// Version is the current release version.
|
||||
func Version() string {
|
||||
return "1.2.16"
|
||||
return "1.2.18"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user