Files

168 lines
3.8 KiB
Go

package sqimple
import (
"database/sql"
"errors"
"fmt"
"code.aneur.in/go/record"
)
func ScanOneRecord(rows *sql.Rows) (record.Record, error) {
records, err := ScanRecords(rows)
if err != nil {
return nil, err
}
if len(records) == 0 {
return nil, errors.New("no rows")
}
return records[0], nil
}
// ScanRecords transforms SQL row data into record.Record objects, making them easier to handle with type safety.
//
// https://go.dev/ref/spec#Types
func ScanRecords(rows *sql.Rows) ([]record.Record, error) {
columns, err := rows.ColumnTypes()
if err != nil {
return nil, err
}
data := []record.Record{}
for rows.Next() {
values := []any{}
for _, column := range columns {
switch column.ScanType().Name() {
case "bool":
var value bool
values = append(values, &value)
case "byte":
var value byte
values = append(values, &value)
case "complex64":
var value complex64
values = append(values, &value)
case "complex128":
var value complex128
values = append(values, &value)
case "float32":
var value float32
values = append(values, &value)
case "float64":
var value float64
values = append(values, &value)
case "int":
var value int
values = append(values, &value)
case "int8":
var value int8
values = append(values, &value)
case "int16":
var value int16
values = append(values, &value)
case "int32":
var value int32
values = append(values, &value)
case "int64":
var value int64
values = append(values, &value)
case "rune":
var value rune
values = append(values, &value)
case "string":
var value string
values = append(values, &value)
case "uint":
var value uint
values = append(values, &value)
case "uint8":
var value uint8
values = append(values, &value)
case "uint16":
var value uint16
values = append(values, &value)
case "uint32":
var value uint32
values = append(values, &value)
case "uint64":
var value uint64
values = append(values, &value)
default:
return nil, fmt.Errorf("unknown scan type %q", column.ScanType().Name())
}
}
err := rows.Scan(values...)
if err != nil {
return nil, err
}
row := record.Record{}
for i, column := range columns {
switch column.ScanType().Name() {
case "bool":
value := values[i].(*bool)
row[column.Name()] = *value
case "byte":
value := values[i].(*byte)
row[column.Name()] = *value
case "complex64":
value := values[i].(*complex64)
row[column.Name()] = *value
case "complex128":
value := values[i].(*complex128)
row[column.Name()] = *value
case "float32":
value := values[i].(*float32)
row[column.Name()] = *value
case "float64":
value := values[i].(*float64)
row[column.Name()] = *value
case "int":
value := values[i].(*int)
row[column.Name()] = *value
case "int8":
value := values[i].(*int8)
row[column.Name()] = *value
case "int16":
value := values[i].(*int16)
row[column.Name()] = *value
case "int32":
value := values[i].(*int32)
row[column.Name()] = *value
case "int64":
value := values[i].(*int64)
row[column.Name()] = *value
case "rune":
value := values[i].(*rune)
row[column.Name()] = *value
case "string":
value := values[i].(*string)
row[column.Name()] = *value
case "uint":
value := values[i].(*uint)
row[column.Name()] = *value
case "uint8":
value := values[i].(*uint8)
row[column.Name()] = *value
case "uint16":
value := values[i].(*uint16)
row[column.Name()] = *value
case "uint32":
value := values[i].(*uint32)
row[column.Name()] = *value
case "uint64":
value := values[i].(*uint64)
row[column.Name()] = *value
}
}
data = append(data, row)
}
return data, nil
}