Приведение типа Go завершается ошибкой, несмотря на тот же тип

Я использую драйвер jackc/pgx вместе с библиотекой GORM для взаимодействия с базой данных PostgreSQL.

У меня есть случай, когда мне нужно проверить код ошибки PostgreSQL и по-разному обработать определенный тип ошибки. При использовании драйвера pgx методы GORM возвращают тип *pgconn.PgError в качестве error, который содержит поле с конкретным кодом ошибки.

Чтобы получить доступ к этому полю, я должен привести error к *pgconn.PgError, но по какой-то причине это не удается:

res := tx.Take(&f, "id = ?", id)
if res.Error != nil {
    if pqErr, ok := res.Error.(*pgconn.PgError); ok {
        // does not reach here
    } else {
        fmt.Printf("Error type: %T\n", res.Error)
        // Output: "Error type: *pgconn.PgError"
    }
}

Примечания:

  1. Пакеты pgx и pgconn находятся внутри одного и того же проекта, поэтому дело не в том, что они возвращают разные версии типа с одним и тем же именем. Другими словами, у меня есть только один импорт в моем go.mod.
  2. Возвращаемое значение не равно нулю.
  3. Отладчик показывает, что это тип *pgconn.PgError.

Вы пытались использовать отладчик для проверки значений во время выполнения?

Hymns For Disco 15.02.2023 02:40

@HymnsForDisco Да, отладчик тоже говорит, что тип *pgconn.PgError

robbieperry22 15.02.2023 02:44

Пробовали ли вы использовать Reflect для проверки полного пути к типу, т. е. reflect.TypeOf(res.Error).PkgPath()?

Hymns For Disco 15.02.2023 03:02
github.com/jackc/pgconn и github.com/jackc/pgx/pgconn могут сосуществовать в вашей программе. Как правило, пакеты с одним и тем же именем могут существовать в одной и той же программе, если у них разные пути импорта.
Hymns For Disco 15.02.2023 03:07

@HymnsForDisco ах, спасибо! Не уверен, как бы я пришел к этому решению, поскольку отражение PkgPath() возвращает пустую строку, а go.mod из pgx не показывает, что он импортирует github.com/jackc/pgconn.

robbieperry22 15.02.2023 19:25
Типы данных JavaScript
Типы данных JavaScript
В JavaScript существует несколько типов данных, включая примитивные типы данных и ссылочные типы данных. Вот краткое объяснение различных типов данных...
Как сделать движок для футбольного матча? (простой вариант)
Как сделать движок для футбольного матча? (простой вариант)
Футбол. Для многих людей, живущих на земле, эта игра - больше, чем просто спорт. И эти люди всегда мечтают стать футболистом или менеджером. Но, к...
Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик Модуль flexbox, также известный как гибкий модуль разметки box, помогает эффективно проектировать и...
Введение в раздел "Заголовок" в HTML
Введение в раздел "Заголовок" в HTML
Говорят, что лучшее о человеке можно увидеть только изнутри, и это относится и к веб-страницам HTML! Причина, по которой некоторые веб-страницы не...
1
5
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Как правильно указал @HymnsForDisco в комментариях, существуют и github.com/jackc/pgconn, и github.com/jackc/pgx/pgconn. Оказывается, возвращенная ошибка была из первого, тогда как я импортировал последний в свой код.

Для подтверждения я добавил следующую строку:

fmt.Println("Error path: ", reflect.TypeOf(res.Error).Elem().PkgPath())
// Output: "Error path:  github.com/jackc/pgconn"

Изменение моего импорта на «github.com/jackc/pgconn» решило проблему.

Ответ принят как подходящий

Вы решили свою собственную проблему, но вот, возможно, полезная информация и то, как я нашел источник.

Пакеты с одинаковыми именами могут существовать в одной и той же программе, если у них разные пути импорта. Например, в стандартной библиотеке есть как math/rand, так и crypto/rand, каждая из которых называется rand. Это первый намек на то, что *pgconn.PgError и *pgconn.PgError не одно и то же: они происходят из разных путей импорта.

Когда модули в Go вносят серьезные изменения , они должны изменить свой путь импорта. Это необходимо для сохранения обратной совместимости в отношении путей импорта. Обратите внимание, что обычно это делается путем обновления объявления module в файле go.mod, а не путем фактического перемещения кода в подкаталог. Например, посмотрите этот коммит, где pgx был перемещен с v4 на v5. Это вторая подсказка: код из проекта pgx доступен по нескольким путям импорта (из-за нескольких основных версий).

Имея это в виду, я использовал теги git для просмотра репозитория в последней версии v4.x.x . Я заметил, что, как ни странно, пакета pgconn не существовало в v4. Это, казалось, исключало идею конфликта github.com/jackc/pgx/v4/pgconn vs github.com/jackc/pgx/v5/pgconn. Затем я поискал в Google «pgconn» и нашел репозиторий github.com/jackc/pgconn, где я увидел в README:

Эта версия используется с pgx v4. В pgx v5 он является частью репозитория https://github.com/jackc/pgx.

Судя по другой информации, которую вы предоставили, ваша ошибка могла заключаться в использовании пути импорта "github.com/jackc/pgx/pgconn". Как показано в примере кода для pgx, текущий путь импорта, который вы должны использовать для базового модуля, — "github.com/jackc/pgx/v5", и пакеты внутри него будут указываться аналогичным образом, например, "github.com/jackc/pgx/v5/pgconn".

Другие вопросы по теме