Приведение типа 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.

🤔 А знаете ли вы, что...
Go имеет средства для создания и использования модулей (modules) для управления зависимостями.


1
51
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".