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

Out может использоваться для извлечения параметров OUTPUT из хранимых процедур. В этом разделе представлены другие типы: RawBytes, Result

type Out

type Out struct {

    // Dest — указатель на значение, которое будет установлено в качестве результата
    // параметра OUTPUT хранящейся процедуры.
    Dest any

    // In — является ли параметр параметром INOUT. Если да, то входное значение для хранящейся
    // процедуры — это разыменованное значение указателя Dest, которое затем заменяется
	// выходным значением.
    In bool
	// содержит отфильтрованные или неэкспортированные поля
}

Out может использоваться для извлечения параметров OUTPUT из хранимых процедур.

Не все драйверы и базы данных поддерживают параметры OUTPUT.

Пример использования:

var outArg string
_, err := db.ExecContext(ctx, «ProcName», sql.Named(«Arg1», sql.Out{Dest: &outArg}))

type RawBytes

type RawBytes []byte

RawBytes — это байтовый срез, который содержит ссылку на память, принадлежащую самой базе данных. После выполнения Rows.Scan в RawBytes срез остается действительным только до следующего вызова Rows.Next, Rows.Scan или Rows.Close.

RawBytes - это специальный тип в пакете database/sql, определённый как []byte, который используется для сканирования данных из базы данных без копирования памяти. Это может быть полезно для оптимизации производительности при работе с большими бинарными данными.

Основные принципы работы с RawBytes

  1. Временное владение памятью: RawBytes содержит ссылку на память, управляемую драйвером БД, а не на копию данных.
  2. Ограниченное время жизни: Данные в RawBytes действительны только до следующего вызова методов:
    • Next()
    • Scan()
    • Close()
  3. Небезопасность: Если сохранить RawBytes и попытаться использовать после вышеуказанных вызовов, это приведёт к неопределённому поведению.

Пример 1: Базовое сканирование

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

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

    rows, err := db.Query("SELECT blob_column FROM my_table WHERE id = ?", 1)
    if err != nil {
        panic(err)
    }
    defer rows.Close()

    for rows.Next() {
        var raw sql.RawBytes
        if err := rows.Scan(&raw); err != nil {
            panic(err)
        }
        // Используем raw сразу, пока он действителен
        fmt.Printf("Data: %s\n", raw)
        // Если нужно сохранить данные, нужно сделать копию:
        dataCopy := make([]byte, len(raw))
        copy(dataCopy, raw)
        // Теперь dataCopy можно использовать после rows.Next()
    }
}

Пример 2: Сканирование нескольких столбцов

func scanMultipleColumns(db *sql.DB) error {
    rows, err := db.Query("SELECT id, name, data FROM documents")
    if err != nil {
        return err
    }
    defer rows.Close()

    for rows.Next() {
        var id int
        var name string
        var data sql.RawBytes
        
        if err := rows.Scan(&id, &name, &data); err != nil {
            return err
        }
        
        fmt.Printf("ID: %d, Name: %s, Data length: %d\n", id, name, len(data))
        
        // Обработка данных должна быть здесь
        processData(data)
    }
    return rows.Err()
}

func processData(data []byte) {
    // Обработка данных
}

Пример 3: Опасное использование (неправильное)

func incorrectUsage(db *sql.DB) {
    rows, err := db.Query("SELECT data FROM large_blobs")
    if err != nil {
        panic(err)
    }
    defer rows.Close()

    var allData [][]byte
    
    for rows.Next() {
        var raw sql.RawBytes
        if err := rows.Scan(&raw); err != nil {
            panic(err)
        }
        // ОШИБКА: сохраняем RawBytes, который станет недействительным
        allData = append(allData, raw)
    }
    
    // Здесь allData содержит недействительные данные!
    for _, data := range allData {
        fmt.Println(data) // Может привести к панике или неверным данным
    }
}

Когда использовать RawBytes

  1. Для больших бинарных данных, когда копирование нежелательно
  2. Для временной обработки данных, которые не нужно сохранять
  3. Когда производительность критична, и вы готовы следить за временем жизни данных

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

Если нужно сохранить данные:

var raw sql.RawBytes
var data []byte

rows.Scan(&raw)
data = make([]byte, len(raw))
copy(data, raw)
// Теперь data можно использовать в любом месте

type Result ¶

type Result интерфейс {
    // LastInsertId возвращает целое число, сгенерированное базой данных
	// в ответ на команду. Обычно это будет из
    // столбца «автоинкремент» при вставке новой строки. Не все
    // базы данных поддерживают эту функцию, и синтаксис таких
    // операторов варьируется.
    LastInsertId() (int64, error)

    // RowsAffected возвращает количество строк, затронутых
    // обновлением, вставкой или удалением. Не все базы данных или драйверы баз данных
	// драйвер базы данных может поддерживать эту функцию.
    RowsAffected() (int64, error)
}

Результат обобщает выполненную команду SQL.