Описание типа DBStats database/sql

DBStats DBStats содержит статистику базы данных. В этом разделе представлены другие типы: IsolationLevel, NamedArg, Null, NullBool

type DBStats


type DBStats struct {
	MaxOpenConnections int // Максимальное количество открытых соединений с базой данных.

	// Состояние пула
	OpenConnections int // Количество установленных соединений, как используемых, так и простаивающих.
	InUse int // Количество соединений, используемых в данный момент.
	Idle int // Количество простаивающих соединений.

	// Счетчики
	WaitCount int64 // Общее количество ожидающих соединений.
	WaitDuration time.Duration // Общее время, заблокированное в ожидании нового соединения.
	MaxIdleClosed int64 // Общее количество соединений, закрытых из-за SetMaxIdleConns.
	MaxIdleTimeClosed int64 // Общее количество соединений, закрытых из-за SetConnMaxIdleTime.
	MaxLifetimeClosed int64 // Общее количество соединений, закрытых из-за SetConnMaxLifetime.
}

DBStats содержит статистику базы данных.

type IsolationLevel

type IsolationLevel int

IsolationLevel - это уровень изоляции транзакции, используемый в TxOptions.

const (
	LevelDefault IsolationLevel = iota
	LevelReadUncommitted
	LevelReadCommitted
	LevelWriteCommitted
	LevelRepeatableRead
	LevelSnapshot
	LevelSerializable
	LevelLinearizable
)

Различные уровни изоляции, которые драйверы могут поддерживать в DB.BeginTx. Если драйвер не поддерживает заданный уровень изоляции, может быть возвращена ошибка.

См. https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels.

func (IsolationLevel) String

func (i IsolationLevel) String() string

String возвращает имя уровня изоляции транзакции.

type NamedArg

type NamedArg struct {

	// Name - имя параметра-заместителя.
	//
    // Если пусто, то будет использоваться
    // порядковая позиция в списке аргументов.
	//
    // Имя не должно содержать префикса символа.
	Name string

	// Value - это значение параметра.
	// Ему могут быть присвоены те же типы значений, что и аргументам запроса
    //.
	Value any
    // содержит отфильтрованные или неэкспонированные поля
}

NamedArg - это именованный аргумент. Значения NamedArg могут использоваться в качестве аргументов DB.Query или DB.Exec и связываться с соответствующим именованным параметром в операторе SQL.

Для более краткого способа создания значений NamedArg см. функцию Named.

func Named

func Named(name string, value any) NamedArg

Named предоставляет более лаконичный способ создания значений NamedArg.

Пример
db.ExecContext(ctx, `
 delete from Invoice
 where
 TimeCreated < @end
 and TimeCreated >= @start;`,
    sql.Named("start", startTime),
    sql.Named("end", endTime),
)

type Null

type Null[T any] struct {
V T
Valid bool
}

Null представляет значение, которое может быть нулевым. Null реализует интерфейс Scanner, поэтому его можно использовать в качестве места сканирования:

var s Null[string]
err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
...
if s.Valid {
 // используем s.V
} else {
 // NULL значение
}

T должен быть одним из типов, принимаемых driver.Value.

func (*Null[T]) Scan

func (n *Null[T]) Scan(value any) error

func (Null[T]) Value

func (n Null[T]) Value() (driver.Value, error)

type NullBool ¶

type NullBool struct {
	Bool  bool
	Valid bool // Valid is true if Bool is not NULL
}

NullBool представляет bool, который может быть нулевым. NullBool реализует интерфейс Scanner, поэтому его можно использовать в качестве места сканирования, аналогично NullString.

func (*NullBool) Scan

func (n *NullBool) Scan(value any) error

Scan реализует интерфейс Scanner.

func (NullBool) Value

func (n NullBool) Value() (driver.Value, error)

Value реализует интерфейс driver.Valuer.

type NullByte

type NullByte struct {
    Byte    byte
    Valid   bool // Valid is true if Byte is not NULL
}

NullByte представляет байт, который может быть нулевым. NullByte реализует интерфейс Scanner, поэтому его можно использовать в качестве места сканирования, аналогично NullString.

func (*NullByte) Scan

func (n *NullByte) Scan(value any) error

Scan реализует интерфейс Scanner.

func (NullByte) Value

func (n NullByte) Value() (driver.Value, error)

Value реализует интерфейс driver.Valuer.

type NullFloat64 ¶

type NullFloat64 struct {
    Float64 float64
    Valid bool // Valid is true if Float64 is not NULL
}

NullFloat64 представляет float64, который может быть нулевым. NullFloat64 реализует интерфейс Scanner, поэтому его можно использовать в качестве места сканирования, аналогично NullString.

func (*NullFloat64) Scan ¶

func (n *NullFloat64) Scan(value any) error

Scan реализует интерфейс Scanner.

func (NullFloat64) Value ¶

func (n NullFloat64) Value() (driver.Value, error)

Value реализует интерфейс driver.Valuer.

type NullInt16

type NullInt16 struct {
    Int16 int16
    Valid bool // Valid is true if Int16 is not NULL
}

NullInt16 представляет int16, который может быть нулевым. NullInt16 реализует интерфейс Scanner, поэтому его можно использовать в качестве места сканирования, аналогично NullString.

func (*NullInt16) Scan

func (n *NullInt16) Scan(value any) error

Scan реализует интерфейс Scanner.

func (NullInt16) Value

func (n NullInt16) Value() (driver.Value, error)

Value реализует интерфейс driver.Valuer.

type NullInt32

type NullInt32 struct {
    Int32 int32
    Valid bool // Valid is true if Int32 is not NULL
}

NullInt32 представляет int32, который может быть нулевым. NullInt32 реализует интерфейс Scanner, поэтому его можно использовать в качестве места сканирования, аналогично NullString.

func (*NullInt32) Scan

func (n *NullInt32) Scan(value any) error

Scan реализует интерфейс Scanner.

func (NullInt32) Value

func (n NullInt32) Value() (driver.Value, error)

Value реализует интерфейс driver.Valuer.

type NullInt64

type NullInt64 struct {
    Int64 int64
    Valid bool // Valid is true if Int64 is not NULL
}

NullInt64 представляет int64, который может быть нулевым. NullInt64 реализует интерфейс Scanner, поэтому его можно использовать в качестве места сканирования, аналогично NullString.

func (*NullInt64) Scan

func (n *NullInt64) Scan(value any) error

Scan реализует интерфейс Scanner.

func (NullInt64) Value

func (n NullInt64) Value() (driver.Value, error)

Value реализует интерфейс driver.Valuer.

type NullString

type NullString struct {
    String string
    Valid bool // Valid is true if String is not NULL
}

NullString представляет строку, которая может быть нулевой. NullString реализует интерфейс Scanner, поэтому его можно использовать в качестве места сканирования:

var s NullString
err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
...
if s.Valid {
 // использовать s.String
} else {
 // NULL значение
}

func (*NullString) Scan

func (ns *NullString) Scan(value any) error

Scan реализует интерфейс Scanner.

func (NullString) Value

func (ns NullString) Value() (driver.Value, error)

Value реализует интерфейс driver.Valuer.

type NullTime

type NullTime struct {
    Time.Time
    Valid bool // Valid is true if Time is not NULL
}

NullTime представляет время time.Time, которое может быть нулевым. NullTime реализует интерфейс Scanner, поэтому его можно использовать в качестве места сканирования, аналогично NullString.

func (*NullTime) Scan

func (n *NullTime) Scan(value any) error

Scan реализует интерфейс Scanner.

func (NullTime) Value

func (n NullTime) Value() (driver.Value, error)

Value реализует интерфейс driver.Valuer.

Практическое использование Null-типов в Go

Null-типы (NullString, NullInt64, NullTime и др.) нужны для работы с NULL-значениями из базы данных. Разберём на реальных примерах.

Интерфейсы

  1. Scanner - позволяет сканировать (читать) значение из БД
  2. driver.Valuer - позволяет преобразовывать значение для записи в БД

Полный пример с NullString и NullTime

Пример
package main

import (
	"database/sql"
	"log"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

type UserProfile struct {
	ID        int64
	Name      sql.NullString
	Bio       sql.NullString
	DeletedAt sql.NullTime
}

func main() {
	db, err := sql.Open("mysql", "user:password@/dbname")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// 1. Запись данных с NULL-значениями
	profile := UserProfile{
		Name:      sql.NullString{String: "Иван Иванов", Valid: true},
		Bio:       sql.NullString{}, // NULL
		DeletedAt: sql.NullTime{Time: time.Now(), Valid: false}, // Будет записан как NULL
	}

	_, err = db.Exec(
		"INSERT INTO users (name, bio, deleted_at) VALUES (?, ?, ?)",
		profile.Name,
		profile.Bio,
		profile.DeletedAt,
	)
	if err != nil {
		log.Fatal(err)
	}

	// 2. Чтение данных с возможными NULL-значениями
	var user UserProfile
	err = db.QueryRow(`
		SELECT id, name, bio, deleted_at 
		FROM users 
		WHERE id = ?
	`, 1).Scan(
		&user.ID,
		&user.Name,
		&user.Bio,
		&user.DeletedAt,
	)
	if err != nil {
		log.Fatal(err)
	}

	// 3. Обработка NULL-значений
	log.Println("ID:", user.ID)
	
	if user.Name.Valid {
		log.Println("Name:", user.Name.String)
	} else {
		log.Println("Name not set")
	}
	
	if user.Bio.Valid {
		log.Println("Bio:", user.Bio.String)
	} else {
		log.Println("Bio not set")
	}
	
	if user.DeletedAt.Valid {
		log.Println("Deleted at:", user.DeletedAt.Time)
	} else {
		log.Println("Not deleted")
	}
}

Как работают Null-типы?

1. Scan (чтение из БД)

Пример
func (ns *NullString) Scan(value interface{}) error {
	if value == nil {
		ns.String, ns.Valid = "", false
		return nil
	}
	ns.Valid = true
	return convertAssign(&ns.String, value)
}

2. Value (запись в БД)

Пример
func (ns NullString) Value() (driver.Value, error) {
	if !ns.Valid {
		return nil, nil
	}
	return ns.String, nil
}

Пример с пользовательским Null-типом

Создадим свой NullStatus для enum-поля:

Пример
type NullStatus struct {
	Status string
	Valid  bool
}

func (ns *NullStatus) Scan(value interface{}) error {
	if value == nil {
		ns.Status, ns.Valid = "", false
		return nil
	}
	ns.Valid = true
	switch v := value.(type) {
	case []byte:
		ns.Status = string(v)
	case string:
		ns.Status = v
	default:
		return fmt.Errorf("unsupported type: %T", value)
	}
	return nil
}

func (ns NullStatus) Value() (driver.Value, error) {
	if !ns.Valid {
		return nil, nil
	}
	return ns.Status, nil
}

Когда использовать Null-типы?

  1. Когда поле в БД может быть NULL
  2. Когда нужно отличать “нулевое значение” от “неустановленного”
  3. При работе с опциональными полями

Альтернативы

В новых версиях Go можно использовать указатели:

Пример
var name *string
err := db.QueryRow("SELECT name FROM users WHERE id = 1").Scan(&name)
if name != nil {
    // есть значение
}

Но Null-типы предоставляют более удобный интерфейс и явный флаг Valid.