Ошибка синтаксиса SQL
Создано: 28 ноября 2024 г.
Отвечено с использованием O1 Preview от Chat01
Создано: 28 ноября 2024 г.
Отвечено с использованием O1 Preview от Chat01
package repository
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/Masterminds/squirrel"
_ "github.com/lib/pq"
"log"
"os"
"reflect"
"repository/internal/models"
"strings"
)
type UserStorage struct {
db *sql.DB
}
func NewUserStorage() *UserStorage {
driver, name, pass, dbName := os.Getenv("DB_DRIVER"), os.Getenv("DB_NAME"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME")
textdb, err := sql.Open(driver, fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", name, pass, dbName)) if err != nil { log.Fatal(fmt.Sprintf("Database connection failed: %s", err)) } // Создаем таблицу в БД, если её нет user := models.UserDto{} reflectTable := reflect.TypeOf(user) var fields []string for i := 0; i < reflectTable.NumField(); i++ { field := reflectTable.Field(i) fields = append(fields, field.Tag.Get("json")+" "+field.Tag.Get("db_type")) } result := fmt.Sprintf("CREATE TABLE users (%s)", strings.Join(fields, ", ")) _, err = db.Exec(result) if err != nil { log.Print("Таблица users уже создана") } return &UserStorage{db: db}
}
func (us *UserStorage) Create(ctx context.Context, user models.UserDto) error {
sq, args, err := squirrel.Insert("users").Columns("id", "login", "password", "role", "del", "deleted_at").
Values(user.Id, user.Login, user.Pass, user.Role, user.Del, user.DeletedAt).ToSql()
if err != nil {
return errors.New(fmt.Sprintf("Ошибка создания пользователя: %s", err))
}
text_, err = us.db.Exec(sq, args...) if err != nil { return errors.New(fmt.Sprintf("Ошибка создания пользователя: %s", err)) } log.Print("Успешное создание пользователя") return nil
}
func (us *UserStorage) GetByID(ctx context.Context, id string) (UserStorage, error) {
//TODO implement me
panic("implement me")
}
func (us *UserStorage) Update(ctx context.Context, user UserStorage) error {
//TODO implement me
panic("implement me")
}
func (us *UserStorage) Delete(ctx context.Context, id string) error {
//TODO implement me
panic("implement me")
}
Что тут не так пишет ошибку
ошибка синтаксиса (примерное положение: ",")
position 68
Ответ дай на русском языке
Проблема в том, что при создании таблицы вы используете рефлексию для получения тегов json
и db_type
из структуры UserDto
. Если в структуре UserDto
отсутствуют или некорректно указаны эти теги, то при генерации SQL-запроса получается некорректный синтаксис.
Например, если теги отсутствуют, то field.Tag.Get("json")
и field.Tag.Get("db_type")
вернут пустые строки. В результате ваш SQL-запрос будет выглядеть примерно так:
sqlCREATE TABLE users (, , , )
Это, естественно, приведет к ошибке синтаксиса.
Как исправить:
Убедитесь, что в структуре UserDto
для каждого поля указаны корректные теги json
и db_type
. Пример:
gotype UserDto struct { Id int `json:"id" db_type:"SERIAL PRIMARY KEY"` Login string `json:"login" db_type:"VARCHAR(255)"` Pass string `json:"password" db_type:"VARCHAR(255)"` Role string `json:"role" db_type:"VARCHAR(50)"` Del bool `json:"del" db_type:"BOOLEAN"` DeletedAt time.Time `json:"deleted_at" db_type:"TIMESTAMP"` }
После этого ваш код для создания таблицы сможет корректно сгенерировать SQL-запрос:
sqlCREATE TABLE users (id SERIAL PRIMARY KEY, login VARCHAR(255), password VARCHAR(255), role VARCHAR(50), del BOOLEAN, deleted_at TIMESTAMP)
Причина ошибки:
json
и db_type
в структуре UserDto
.Рекомендации:
У меня уже есть эта структура
type UserDto struct {
Id int json:"id" db_type:"SERIAL PRIMARY KEY"
Login string json:"login" db_type:"VARCHAR(100)"
Pass string json:"password" db_type:"VARCHAR(100)"
Role string json:"role" db_type:"VARCHAR(100)"
Del bool json:"del" db_type:"BOOLEAN"
DeletedAt time.Time json:"deleted_at" db_type:"TIMESTAMP"
}
Проблема связана с тем, что некоторые имена полей, такие как password
, могут быть зарезервированными словами в SQL (особенно в PostgreSQL). Когда вы генерируете SQL-запрос без экранирования имен полей, это может привести к ошибке синтаксиса.
Ошибка:
textошибка синтаксиса (примерное положение: ",") position 68
Если подсчитать позиции, то 68-я позиция попадает на слово password
в вашем сгенерированном SQL-запросе.
Решение:
Экранируйте имена полей, обернув их в двойные кавычки. Это позволит избежать конфликтов с зарезервированными словами и обеспечит корректный синтаксис SQL-запроса.
Как исправить код:
Измените часть кода, где вы формируете список полей, следующим образом:
gofor i := 0; i < reflectTable.NumField(); i++ { field := reflectTable.Field(i) jsonTag := field.Tag.Get("json") dbType := field.Tag.Get("db_type") // Экранируем имя поля двойными кавычками fields = append(fields, fmt.Sprintf(`"%s" %s`, jsonTag, dbType)) }
Таким образом, ваши имена полей будут выглядеть как "id"
, "login"
, "password"
и т.д.
Пример сгенерированного SQL-запроса:
sqlCREATE TABLE users ("id" SERIAL PRIMARY KEY, "login" VARCHAR(100), "password" VARCHAR(100), "role" VARCHAR(100), "del" BOOLEAN, "deleted_at" TIMESTAMP)
Почему это работает:
Дополнительные рекомендации:
Всегда экранируйте имена таблиц и полей, если есть вероятность совпадения с зарезервированными словами.
Для отладки выводите сгенерированный SQL-запрос перед его выполнением:
golog.Printf("Создаем таблицу с запросом: %s", result)
Убедитесь, что у вас установлены все необходимые права доступа для создания таблиц в базе данных.
Полный исправленный код функции NewUserStorage
:
gofunc NewUserStorage() *UserStorage { driver, name, pass, dbName := os.Getenv("DB_DRIVER"), os.Getenv("DB_NAME"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME") db, err := sql.Open(driver, fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", name, pass, dbName)) if err != nil { log.Fatal(fmt.Sprintf("Database connection failed: %s", err)) } // Создаем таблицу в БД, если её нет user := models.UserDto{} reflectTable := reflect.TypeOf(user) var fields []string for i := 0; i < reflectTable.NumField(); i++ { field := reflectTable.Field(i) jsonTag := field.Tag.Get("json") dbType := field.Tag.Get("db_type") // Экранируем имя поля двойными кавычками fields = append(fields, fmt.Sprintf(`"%s" %s`, jsonTag, dbType)) } result := fmt.Sprintf("CREATE TABLE IF NOT EXISTS users (%s)", strings.Join(fields, ", ")) _, err = db.Exec(result) if err != nil { log.Printf("Ошибка при создании таблицы users: %s", err) } else { log.Print("Таблица users успешно создана или уже существует") } return &UserStorage{db: db} }
Объяснение изменений:
fmt.Sprintf
и двойных кавычек.IF NOT EXISTS
в SQL-запросе CREATE TABLE
, чтобы избежать ошибки, если таблица уже существует.Вывод:
Экранирование имен полей решит проблему синтаксиса и позволит успешно создать таблицу в базе данных.
ОШИБКА
КОд 42601
Ошибка с кодом 42601 в PostgreSQL означает синтаксическую ошибку в SQL-запросе. Давайте попробуем найти и исправить эту ошибку.
1. Вывод сгенерированного SQL-запроса
Прежде всего, нам нужно увидеть точный SQL-запрос, который генерируется вашим кодом. Добавьте вывод запроса перед его выполнением:
golog.Printf("Создаем таблицу с запросом: %s", result)
Это поможет нам увидеть, что именно генерируется и где может быть ошибка.
2. Возможные причины ошибки
Экранирование имени таблицы: Возможно, проблема возникает из-за имени таблицы users
, которое может конфликтовать с системными таблицами или зарезервированными словами. Экранируйте имя таблицы двойными кавычками:
goresult := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS "%s" (%s)`, "users", strings.Join(fields, ", "))
Тип данных для time.Time
: Ваша структура UserDto
содержит поле DeletedAt
типа time.Time
с типом базы данных TIMESTAMP
. При использовании time.Time
необходимо убедиться, что значение по умолчанию корректно, или заменить тип на NULLABLE TIMESTAMP
.
Отсутствие запятой или неправильный синтаксис: Возможно, где-то в сгенерированном SQL-запросе пропущена запятая или есть лишний символ.
3. Проверка типов данных
Убедитесь, что все типы данных соответствуют тем, которые поддерживает PostgreSQL. Например, для автогенерируемого первичного ключа обычно используется SERIAL
или BIGSERIAL
. Однако начиная с PostgreSQL 10 рекомендуется использовать GENERATED ALWAYS AS IDENTITY
.
Обновите тип данных для поля Id
:
goId int `json:"id" db_type:"INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY"`
4. Полный пример исправленного кода
Вот обновленная функция NewUserStorage
с учетом вышеизложенного:
gofunc NewUserStorage() *UserStorage { driver, name, pass, dbName := os.Getenv("DB_DRIVER"), os.Getenv("DB_NAME"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME") db, err := sql.Open(driver, fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", name, pass, dbName)) if err != nil { log.Fatal(fmt.Sprintf("Database connection failed: %s", err)) } // Создаем таблицу в БД, если её нет user := models.UserDto{} reflectTable := reflect.TypeOf(user) var fields []string for i := 0; i < reflectTable.NumField(); i++ { field := reflectTable.Field(i) jsonTag := field.Tag.Get("json") dbType := field.Tag.Get("db_type") // Экранируем имя поля двойными кавычками fields = append(fields, fmt.Sprintf(`"%s" %s`, jsonTag, dbType)) } // Экранируем имя таблицы и добавляем IF NOT EXISTS result := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS "%s" (%s)`, "users", strings.Join(fields, ", ")) // Выводим сгенерированный SQL-запрос log.Printf("Создаем таблицу с запросом: %s", result) _, err = db.Exec(result) if err != nil { log.Printf("Ошибка при создании таблицы users: %s", err) } else { log.Print("Таблица users успешно создана или уже существует") } return &UserStorage{db: db} }
5. Проверка выполнения запроса
После внесения изменений запустите код и внимательно посмотрите на вывод сгенерированного SQL-запроса. Если ошибка все еще возникает, обратите внимание на позицию ошибки, которую сообщает PostgreSQL, и сопоставьте ее с позицией в SQL-запросе.
6. Пример возможного SQL-запроса
После внесенных изменений SQL-запрос должен выглядеть примерно так:
sqlCREATE TABLE IF NOT EXISTS "users" ( "id" INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, "login" VARCHAR(100), "password" VARCHAR(100), "role" VARCHAR(100), "del" BOOLEAN, "deleted_at" TIMESTAMP )
7. Дополнительные советы
Проверка зарезервированных слов: Убедитесь, что ни названия полей, ни названия таблиц не совпадают с зарезервированными словами PostgreSQL. Если совпадают, экранирование должно решить проблему.
Соответствие типов данных: Убедитесь, что типы данных в тегах db_type
соответствуют тем, которые ожидает PostgreSQL.
Правильность синтаксиса: Проверьте, нет ли лишних или недостающих запятых, скобок или других символов.
8. Если проблема сохраняется
Если после всех этих изменений ошибка все еще возникает, пожалуйста, предоставьте:
log.Printf
.Это позволит более точно определить проблему и предложить конкретное решение.
9. Пример проверки SQL-запроса в консоли PostgreSQL
Вы можете взять сгенерированный SQL-запрос и попробовать выполнить его напрямую в консоли PostgreSQL или через инструмент управления базой данных (например, pgAdmin). Это поможет определить, является ли проблема с синтаксисом или с кодом Go.
Вывод
Ошибка с кодом 42601 указывает на синтаксическую ошибку в SQL-запросе. Экранирование имен таблиц и полей, проверка соответствия типов данных и вывод сгенерированного SQL-запроса для отладки должны помочь решить проблему.
Может проблема в том, что я делаю это для postgres и там нужжно как-то по другому написать
Squirrel loves PostgreSQL:
psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)
// You use question marks for placeholders...
sql, _, _ := psql.Select("*").From("elephants").Where("name IN (?,?)", "Dumbo", "Verna").ToSql()
/// ...squirrel replaces them using PlaceholderFormat.
sql == "SELECT * FROM elephants WHERE name IN (2)"
/// You can retrieve id ...
query := sq.Insert("nodes").
Columns("uuid", "type", "data").
Values(node.Uuid, node.Type, node.Data).
Suffix("RETURNING "id"").
RunWith(m.db).
PlaceholderFormat(sq.Dollar)
query.QueryRow().Scan(&node.id)
You can escape question marks by inserting two question marks:
SELECT * FROM nodes WHERE meta->'format' ??| array[?,?]
will generate with the Dollar Placeholder:
SELECT * FROM nodes WHERE meta->'format' ?| array[2]
Мой код:
package repository
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/Masterminds/squirrel"
_ "github.com/lib/pq"
"log"
"os"
"reflect"
"repository/internal/models"
"strings"
)
type UserStorage struct {
db *sql.DB
}
func NewUserStorage() *UserStorage {
driver, name, pass, dbName := os.Getenv("DB_DRIVER"), os.Getenv("DB_NAME"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME")
textdb, err := sql.Open(driver, fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", name, pass, dbName)) if err != nil { log.Fatal(fmt.Sprintf("Database connection failed: %s", err)) } // Создаем таблицу в БД, если её нет user := models.UserDto{} reflectTable := reflect.TypeOf(user) var fields []string for i := 0; i < reflectTable.NumField(); i++ { field := reflectTable.Field(i) jsonTag := field.Tag.Get("json") dbType := field.Tag.Get("db_type") fields = append(fields, fmt.Sprintf(`"%s" %s`, jsonTag, dbType)) } result := fmt.Sprintf("CREATE TABLE users (%s)", strings.Join(fields, ", ")) _, err = db.Exec(result) if err != nil { log.Print("Таблица users уже создана") } return &UserStorage{db: db}
}
func (us *UserStorage) Create(ctx context.Context, user models.UserDto) error {
sq, args, err := squirrel.Insert("users").Columns("id", "login", "password", "role", "del").
Values(user.Id, user.Login, user.Pass, user.Role, user.Del).ToSql()
if err != nil {
return errors.New(fmt.Sprintf("Ошибка создания пользователя: %s", err))
}
text_, err = us.db.Exec(sq, args...) if err != nil { return errors.New(fmt.Sprintf("Ошибка создания пользователя: %s", err)) } log.Print("Успешное создание пользователя") return nil
}
func (us *UserStorage) GetByID(ctx context.Context, id string) (UserStorage, error) {
//TODO implement me
panic("implement me")
}
func (us *UserStorage) Update(ctx context.Context, user UserStorage) error {
//TODO implement me
panic("implement me")
}
func (us *UserStorage) Delete(ctx context.Context, id string) error {
//TODO implement me
panic("implement me")
}
Ответ дай на русском языке
Проблема связана с тем, что по умолчанию Squirrel использует знак вопроса ?
в качестве плейсхолдера для параметров в SQL-запросах. Однако PostgreSQL ожидает плейсхолдеры в формате $1
, $2
и т.д. Поэтому, при использовании Squirrel с PostgreSQL, необходимо явно установить формат плейсхолдеров на squirrel.Dollar
.
В вашем коде вы не устанавливаете формат плейсхолдеров, поэтому Squirrel генерирует SQL-запросы с ?
, что приводит к ошибке синтаксиса (код ошибки 42601).
Как исправить:
Установите формат плейсхолдеров для PostgreSQL:
При построении запросов с помощью Squirrel добавьте метод PlaceholderFormat(squirrel.Dollar)
.
Используйте общий билдер запросов для хранилища:
Создайте заранее настроенный билдер запросов для вашего UserStorage
.
Исправленный код:
gopackage repository import ( "context" "database/sql" "fmt" "github.com/Masterminds/squirrel" _ "github.com/lib/pq" "log" "os" "reflect" "repository/internal/models" "strings" ) type UserStorage struct { db *sql.DB queryBuilder squirrel.StatementBuilderType } func NewUserStorage() *UserStorage { driver, name, pass, dbName := os.Getenv("DB_DRIVER"), os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME") connStr := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", name, pass, dbName) db, err := sql.Open(driver, connStr) if err != nil { log.Fatal(fmt.Sprintf("Не удалось подключиться к базе данных: %s", err)) } // Создаем таблицу в БД, если её нет user := models.UserDto{} reflectTable := reflect.TypeOf(user) var fields []string for i := 0; i < reflectTable.NumField(); i++ { field := reflectTable.Field(i) jsonTag := field.Tag.Get("json") dbType := field.Tag.Get("db_type") fields = append(fields, fmt.Sprintf(`"%s" %s`, jsonTag, dbType)) } result := fmt.Sprintf("CREATE TABLE IF NOT EXISTS users (%s)", strings.Join(fields, ", ")) _, err = db.Exec(result) if err != nil { log.Printf("Ошибка при создании таблицы users: %s", err) } else { log.Print("Таблица users успешно создана или уже существует") } // Настраиваем билдер запросов для PostgreSQL queryBuilder := squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) return &UserStorage{ db: db, queryBuilder: queryBuilder, } } func (us *UserStorage) Create(ctx context.Context, user models.UserDto) error { // Используем настроенный билдер запросов query := us.queryBuilder.Insert("users"). Columns("id", "login", "password", "role", "del"). Values(user.Id, user.Login, user.Pass, user.Role, user.Del) // Генерируем SQL-запрос и аргументы sqlStr, args, err := query.ToSql() if err != nil { return fmt.Errorf("Ошибка создания пользователя: %s", err) } // Выполняем запрос _, err = us.db.Exec(sqlStr, args...) if err != nil { return fmt.Errorf("Ошибка создания пользователя: %s", err) } log.Print("Успешное создание пользователя") return nil } // Остальные методы остаются без изменений func (us *UserStorage) GetByID(ctx context.Context, id string) (UserStorage, error) { //TODO implement me panic("implement me") } func (us *UserStorage) Update(ctx context.Context, user UserStorage) error { //TODO implement me panic("implement me") } func (us *UserStorage) Delete(ctx context.Context, id string) error { //TODO implement me panic("implement me") }
Объяснение изменений:
Настройка плейсхолдеров для PostgreSQL:
В NewUserStorage
мы создаем queryBuilder
, используя squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)
, и сохраняем его в структуре UserStorage
.
Использование queryBuilder
в методе Create
:
При построении запроса в методе Create
мы используем us.queryBuilder
, что гарантирует использование правильного формата плейсхолдеров.
Генерация SQL-запроса и аргументов:
Метод ToSql()
возвращает сгенерированный SQL-запрос и слайс аргументов. Это позволяет точно увидеть, какой запрос будет выполнен.
Обработка ошибок:
Мы используем fmt.Errorf
для форматирования ошибок, что является более идиоматичным в Go.
Дополнительные рекомендации:
Проверьте параметры подключения к базе данных:
Убедитесь, что вы используете правильные переменные окружения для имени пользователя и базы данных (DB_USER
, DB_PASSWORD
, DB_NAME
).
Выводите сгенерированные SQL-запросы для отладки:
Для проверки корректности можно добавить вывод сгенерированного SQL-запроса:
golog.Printf("SQL Query: %s, Args: %v", sqlStr, args)
Убедитесь, что типы данных соответствуют PostgreSQL:
Проверьте, что типы данных в тегах db_type
соответствуют типам PostgreSQL.
Используйте транзакции при необходимости:
Если вам нужно обеспечить атомарность операций, рассмотрите использование транзакций.
Заключение:
Проблема была в несовместимости плейсхолдеров, используемых Squirrel по умолчанию, с требованиями PostgreSQL. Установив формат плейсхолдеров на squirrel.Dollar
, вы сможете успешно выполнять запросы к PostgreSQL без ошибок синтаксиса.
Если после внесения изменений ошибка все еще возникает, пожалуйста, предоставьте текст ошибки и сгенерированный SQL-запрос для более детального анализа.
func (us *UserStorage) GetByID(ctx context.Context, id int) (models.UserDto, error) {
sq, args, err := us.builder.Select("id").From(us.TableName()).Where(squirrel.Eq{"id": id}).ToSql()
if err != nil {
return models.UserDto{}, fmt.Errorf("ошибка получения пользователя: %w", err)
}
textrow, err := us.db.QueryContext(ctx, sq, args...) if err != nil { return models.UserDto{}, fmt.Errorf("ошибка получения пользователя: %w", err) } var user models.UserDto for row.Next() { scanErr := row.Scan(&user.Id, &user.Login, &user.Pass, &user.Role, &user.Del, &user.DeletedAt) if scanErr != nil { return models.UserDto{}, fmt.Errorf("ошибка получения пользователя: %w", err) } } return models.UserDto{}, err
}
Теперь ошибка
sql: expected 1 destination arguments in Scan, not 6
Проблема возникает из-за несоответствия между количеством выбранных столбцов в вашем SQL-запросе и количеством аргументов, в которые вы пытаетесь их сохранить с помощью метода Scan
.
Ошибка:
sql: expected 1 destination arguments in Scan, not 6
Это означает, что запрос возвращает только один столбец, а вы пытаетесь считать данные в шесть переменных.
Причина ошибки:
В вашем SQL-запросе вы выбираете только столбец "id"
:
gosq, args, err := us.builder.Select("id").From(us.TableName()).Where(squirrel.Eq{"id": id}).ToSql()
Однако при считывании результата вы пытаетесь заполнить все поля структуры UserDto
:
goscanErr := row.Scan(&user.Id, &user.Login, &user.Pass, &user.Role, &user.Del, &user.DeletedAt)
Поскольку запрос возвращает только один столбец ("id"
), метод Scan
ожидает только один аргумент для сохранения результата.
Как исправить:
Выберите все необходимые столбцы в запросе.
Измените метод Select
, чтобы он выбирал все поля, которые вам нужны:
gosq, args, err := us.builder. Select("id", "login", "password", "role", "del", "deleted_at"). From(us.TableName()). Where(squirrel.Eq{"id": id}). ToSql()
Или используйте Select("*")
, если хотите выбрать все столбцы:
gosq, args, err := us.builder. Select("*"). From(us.TableName()). Where(squirrel.Eq{"id": id}). ToSql()
Используйте QueryRowContext
для запроса одной строки.
Поскольку вы ожидаете получить только одну запись по id
, лучше использовать QueryRowContext
вместо QueryContext
и цикла for row.Next()
:
gorow := us.db.QueryRowContext(ctx, sq, args...)
Считывайте данные без цикла.
Теперь вы можете сразу вызвать Scan
на объекте row
:
goerr = row.Scan(&user.Id, &user.Login, &user.Pass, &user.Role, &user.Del, &user.DeletedAt) if err != nil { return models.UserDto{}, fmt.Errorf("ошибка получения пользователя: %w", err) }
Возвращайте заполненную структуру UserDto
.
После успешного считывания данных верните пользователя и nil
в качестве ошибки:
goreturn user, nil
Исправленный код функции GetByID
:
gofunc (us *UserStorage) GetByID(ctx context.Context, id int) (models.UserDto, error) { // Выбираем все необходимые столбцы sq, args, err := us.builder. Select("id", "login", "password", "role", "del", "deleted_at"). From(us.TableName()). Where(squirrel.Eq{"id": id}). ToSql() if err != nil { return models.UserDto{}, fmt.Errorf("ошибка формирования запроса: %w", err) } // Выполняем запрос и получаем одну строку row := us.db.QueryRowContext(ctx, sq, args...) var user models.UserDto // Считываем данные без цикла err = row.Scan(&user.Id, &user.Login, &user.Pass, &user.Role, &user.Del, &user.DeletedAt) if err != nil { if err == sql.ErrNoRows { return models.UserDto{}, fmt.Errorf("пользователь с id %d не найден", id) } return models.UserDto{}, fmt.Errorf("ошибка получения пользователя: %w", err) } return user, nil }
Пояснения:
Изменение метода Select
: Теперь вы выбираете все столбцы, соответствующие полям в структуре UserDto
. Это обеспечивает соответствие между количеством выбранных столбцов и количеством аргументов в Scan
.
Использование QueryRowContext
: Этот метод удобен для получения одной строки из базы данных и упрощает код.
Обработка ошибок: Добавлена проверка на sql.ErrNoRows
, чтобы вернуть понятную ошибку, если пользователь не найден.
Дополнительные рекомендации:
Проверьте соответствие имен столбцов и полей структуры:
Убедитесь, что имена столбцов в базе данных соответствуют именам, которые вы используете в методе Select
, и что порядок аргументов в Scan
соответствует порядку столбцов в запросе.
Используйте константы или методы для получения имени таблицы:
Если метод us.TableName()
возвращает имя таблицы, убедитесь, что он работает корректно.
Логирование и отладка:
Для отладки можно вывести сгенерированный SQL-запрос и аргументы:
golog.Printf("SQL Query: %s, Args: %v", sq, args)
Безопасность паролей:
Помните, что хранить пароли в открытом виде небезопасно. Рекомендуется хранить хэшированные пароли и не возвращать их в ответах из функций, где это не требуется.
Заключение:
Ошибка связана с тем, что вы выбирали только один столбец ("id"
) в запросе, но пытались считать шесть значений. Исправив запрос и выбрав все необходимые столбцы, вы решите проблему.