如何解决我应该回滚内部只有 COPY 的事务吗?
我在 Go 后端使用 postgres COPY。复制是事务内部的唯一操作。如果失败,我应该回滚吗?
func (pc *Postgres) Copy(records [][]interface{}) error {
tx,err := pc.db.Begin()
if err != nil {
return errors.Wrap(err,"can't open transaction")
}
stmt,err := tx.Prepare(pq.CopyIn(pc.table,pc.columns...))
if err != nil {
return errors.Wrap(err,"can't prepare stmt")
}
for _,record := range records {
if _,err := stmt.Exec(record...); err != nil {
return errors.Wrap(err,"error exec record")
}
}
if _,err = stmt.Exec(); err != nil {
return errors.Wrap(err,"error exec stmt")
}
if err = stmt.Close(); err != nil {
return errors.Wrap(err,"error close stmt")
}
if err = tx.Commit(); err != nil {
return errors.Wrap(err,"error commit transaction")
}
return nil
}
据我所知,如果 \copy
失败,交易将被中止(link)和 rolled back。
但是在官方 lib/pq
examples 我看到他们总是使用回滚(但他们有不止一个操作)。
有人可以指导我了解这些细微差别吗?
解决方法
看起来混乱的一部分是因为 \COPY
与 COPY
不是一回事。
\COPY
(在 this question 中引用)是 psql 中的一个命令,用于“执行前端(客户端)复制”。简单来说,您在计算机上运行 psql
(基于终端的 PostgreSQL 前端),并且可以在数据库和本地存储的文件(或从您的机器访问)之间\COPY
数据。此命令是 psql
的一部分,不能通过 lib/pq
使用。
COPY
是运行在服务器上的 Postgres SQL 命令;您正在复制的文件“必须可由 PostgreSQL 用户(服务器运行的用户 ID)访问,并且必须从服务器的角度指定名称”。这就是您在应用程序中调用的内容(来自 pq docs“CopyIn 在内部使用 COPY FROM”实现 here 和 here)
所以,正如@mh-cbon 的 answer referenced 所说:
延迟回滚是为了确保在提前返回的情况下回滚事务。
考虑:
tx,err := pc.db.Begin()
if err != nil {
return errors.Wrap(err,"can't open transaction")
}
stmt,err := tx.Prepare(pq.CopyIn(pc.table,pc.columns...))
if err != nil {
return errors.Wrap(err,"can't prepare stmt")
}
如果 Prepare
失败,您已经创建了一个事务,然后返回而不关闭它;这使交易处于打开状态,即 not a good thing。添加 defer tx.Rollback()
可确保不会发生这种情况。