Я использую драйвер 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"
}
}
Примечания:
pgx
и pgconn
находятся внутри одного и того же проекта, поэтому дело не в том, что они возвращают разные версии типа с одним и тем же именем. Другими словами, у меня есть только один импорт в моем go.mod.*pgconn.PgError
.@HymnsForDisco Да, отладчик тоже говорит, что тип *pgconn.PgError
Пробовали ли вы использовать Reflect для проверки полного пути к типу, т. е. reflect.TypeOf(res.Error).PkgPath()?
@HymnsForDisco ах, спасибо! Не уверен, как бы я пришел к этому решению, поскольку отражение PkgPath() возвращает пустую строку, а go.mod из pgx не показывает, что он импортирует github.com/jackc/pgconn.
Как правильно указал @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".
Вы пытались использовать отладчик для проверки значений во время выполнения?