【问题标题】:Unable to set psycopg2 autocommit after shp2pgsql importshp2pgsql 导入后无法设置 psycopg2 自动提交
【发布时间】:2016-12-26 00:03:09
【问题描述】:

我正在使用 shp2pgsql 将 shapefile 加载到 postGIS 数据库中,通过 psql 进行管道传输,包装在这样的 python 子进程中:

command = "shp2pgsql -s 4269 -a -D -W LATIN1 file.shp table | psql -h host -d db -U user"
p=subprocess.Popen(command, shell=True)
p.communicate()

这很完美,输出如下:

Loading objects...
Shapefile type: Polygon
Postgis type: MULTIPOLYGON[2]
SET
SET
BEGIN
COMMIT

没有END 声明,但据我所知ENDCOMMIT 是等价的。

然后我想设置con.autocommit = True 用于与同一数据库的 psycopg2 连接。我收到以下错误:

psycopg2.ProgrammingError: autocommit cannot be used inside a transaction

为什么 psycopg2 报告事务仍在进行中?我应该以不同的方式关闭 psql 事务吗?

如果我不运行 shp2pgsql 子进程命令,con.autocommit 会成功执行。 shp2pgsql 默认情况下会在某处打开事务吗? (http://www.bostongis.com/pgsql2shp_shp2pgsql_quickguide.bqg 不建议这样做)

pg_locks 中不存在表明交易停滞/空闲的相关条目。我不在 shp2pgsql 函数中使用 psycopg2 连接对象。而且,如果我重新创建一个新的连接对象

con = psycopg2.connect(host=db_host, user=db_user, password=db_pass, database=db_name)

在 shp2pgsql 函数之后,con.autocommit=True 工作正常。

编辑:我当然可以在所有 shp2pgsql 导入完成后简单地创建 psycopg2 连接对象,但这在我的代码中并不理想,我宁愿了解发生了什么。

Edit2:在打开 psycopg2 连接后立即设置con.autocommit=True,而不是稍后设置,绕过此错误。

Edit3:添加 MWE

import psycopg2
import os
import subprocess
from glob import glob

def vacuum(con, table=""):
    autocommit_orig = con.autocommit
    con.autocommit = True
    with con.cursor() as cur:
        cur.execute("VACUUM ANALYZE {};".format(table))
    con.autocommit = autocommit_orig

def read_shapefile(path, tablename, srid="4269"):
    command = "shp2pgsql -s {} -a -D -W LATIN1 {} {} | psql -h {} -d {} -U {}".format(srid, path, tablename, host, dbname, user)
    p=subprocess.Popen(command, shell=True)
    p.communicate()

def load_data(con, datapath):
    dir = os.path.join(datapath,dataname)
    shapefiles = glob(os.path.join(dir,"*.shp"))

    for shapefile in shapefiles:
        read_shapefile(shapefile, tablename)

if __name__ == "__main__":
    con = psycopg2.connect(host=db_host, user=db_user, password=db_pass, database=db_name)
    load_data(con, datapath)
    vacuum(con, tablename)

【问题讨论】:

  • 你能为此发布一个 MWE 吗?我想确定我了解代码的布局方式。
  • 我在原帖上面加了一个MWE。

标签: python postgresql psycopg2 psql


【解决方案1】:

我看不出这是在哪里发生的,但根据thisthisthis,事务在第一次向数据库发送命令时开始。

事务是每个连接的,所以psql 不应该让你绊倒。

this advice 之后,我的建议是您在代码中的con.autocommit=True 之前添加一个con.rollback()。这将结束以某种方式开始的隐式事务。如果您仍然拥有您期望的所有数据,那么就会发出SELECT 命令或类似的只读指令。

如果您将 con.rollback()con.autocommit=True 向后移动,它将允许您隔离事务开始的位置,而无需重组您的代码。

这是一个猜测,但也许当psql 更改数据库状态时,psycopg2 会在那个时候开始一个事务?我还没有找到支持这个假设的文档。

【讨论】:

    【解决方案2】:

    只是添加一些我刚刚遇到类似问题时发现的额外信息......

    我试图这样做:

    conn = psycopg2.connect(DB_URL)
    conn.autocommit = True
    with conn, conn.cursor() as cur:
        cur.execute("""
        CREATE DATABASE mydb;
        GRANT ALL PRIVILEGES ON DATABASE mydb TO myuser;
        """)
    

    但我一直收到:
    psycopg2.InternalError: CREATE DATABASE cannot run inside a transaction block

    我尝试了各种方法:

    conn = psycopg2.connect(DB_URL)
    conn.autocommit = True
    conn.set_session(autocommit=True)
    conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
    

    ...他们仍然给出了错误。

    原来问题是试图同时执行两条语句。

    这行得通:

    conn = psycopg2.connect(DB_URL)
    conn.autocommit = True
    with conn, conn.cursor() as cur:
        cur.execute("CREATE DATABASE mydb")
        cur.execute("GRANT ALL PRIVILEGES ON DATABASE mydb TO myuser")
    

    【讨论】:

      猜你喜欢
      • 2020-03-07
      • 2019-09-06
      • 1970-01-01
      • 2014-07-20
      • 1970-01-01
      • 1970-01-01
      • 2012-11-30
      • 2021-12-31
      • 2022-01-10
      相关资源
      最近更新 更多