shp2pgsql导入后无法设置psycopg2自动提交

时间:2016-08-18 22:51:56

标签: python postgresql psycopg2 psql

我正在使用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,而不是以后绕过此错误。

编辑3:添加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)

2 个答案:

答案 0 :(得分:0)

我无法看到这种情况发生在哪里,但根据thisthisthis,事务会在第一次将命令发送到数据库时开始。

交易是按连接进行的,因此psql不应该绊倒你。

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

如果您从con.rollback()向后移动con.autocommit=True,它将允许您在不重新构建代码的情况下隔离交易开始的位置。

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

答案 1 :(得分:0)

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

我试图这样做:

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")