导入 shp2pgsql 后无法设置 psycopg2 自动提交
Unable to set psycopg2 autocommit after shp2pgsql import
我正在使用 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
声明,但据我所知 END
和 COMMIT
是等价的。
然后我想为到同一数据库的 psycopg2 连接设置 con.autocommit = True
。我收到以下错误:
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
中不存在建议 stalled/idle 交易的相关条目。我没有在 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)
我看不出这是在哪里发生的,但是根据 this, this, and this,事务是在第一次向数据库发送命令时开始的。
事务是按连接进行的,因此 psql
不会让您感到困惑。
在 this advice 之后,我的建议是您在代码中的 con.autocommit=True
之前添加一个 con.rollback()
。这将结束以某种方式开始的隐式事务。如果您仍然拥有您期望的所有数据,那么就会发出 SELECT
命令或类似的只读指令。
如果您将 con.rollback()
从 con.autocommit=True
向后移动,它将允许您隔离事务开始的位置,而无需重构您的代码。
这是一个猜测,但也许当 psql
改变数据库状态时 psycopg2 在那个时候开始一个事务?我还没有找到支持这个假设的文档。
只是添加一些我自己刚才遇到类似问题时发现的额外信息...
我正在尝试这样做:
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")
我正在使用 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
声明,但据我所知 END
和 COMMIT
是等价的。
然后我想为到同一数据库的 psycopg2 连接设置 con.autocommit = True
。我收到以下错误:
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
中不存在建议 stalled/idle 交易的相关条目。我没有在 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)
我看不出这是在哪里发生的,但是根据 this, this, and this,事务是在第一次向数据库发送命令时开始的。
事务是按连接进行的,因此 psql
不会让您感到困惑。
在 this advice 之后,我的建议是您在代码中的 con.autocommit=True
之前添加一个 con.rollback()
。这将结束以某种方式开始的隐式事务。如果您仍然拥有您期望的所有数据,那么就会发出 SELECT
命令或类似的只读指令。
如果您将 con.rollback()
从 con.autocommit=True
向后移动,它将允许您隔离事务开始的位置,而无需重构您的代码。
这是一个猜测,但也许当 psql
改变数据库状态时 psycopg2 在那个时候开始一个事务?我还没有找到支持这个假设的文档。
只是添加一些我自己刚才遇到类似问题时发现的额外信息...
我正在尝试这样做:
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")