为什么来自同一sqlalchemy引擎的多个连接产生不同的信息?

时间:2016-02-03 21:13:07

标签: python postgresql sqlalchemy temp-tables

我正在使用PostgreSQL 9.3和SQLAlchemy 1.0.11

我的代码如下:

import sqlalchemy as sa

engine = sa.create_engine('postgresql+psycopg2://me@myhost/mydb')
conn = engine.connect()

metadata = sa.MetaData()

# Real table has more columns
mytable = sa.Table(
    'my_temp_table', metadata,
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('something', sa.String(200)),
    prefixes=['TEMPORARY'],
)

metadata.create_all(engine)

pg_conn = engine.raw_connection()
with pg_conn.cursor() as cursor:
    cursor.copy_expert('''COPY my_temp_table (id, something)
                          FROM STDIN WITH CSV''',
                       open('somecsvfile', 'r'))

现在这很好用 - cursor.rowcount报告插入的预期行数。我甚至可以运行cursor.execute('SELECT count(*) FROM my_temp_table'); print(cursor.fetchone()),它会显示相同的#。问题是当我尝试从SQLAlchemy的连接运行查询时,例如

    result = conn.execute(sa.text('SELECT count(*) FROM my_temp_table'))

我把它放在哪里都没关系。我试过几个地方:

  • 在with block
  • 在with block
  • 之外
  • cursor.close()
  • 之后
  • pg_conn.close()
  • 之后

似乎没有任何作用 - 无论我从哪里运行查询,它都是barfs:

sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) relation "my_temp_table" does not exist

有趣的是,如果我将代码包装在try/except中,那么我可以成功地在except块中执行cursor.execute(...)

实际上,现在我已经写出来了,似乎在任何地方使用sqlalchemy连接 都无法看到这些表存在。

那是什么给出的?为什么我的SQLAlchemy连接没有看到这些表,但postgres(engine.raw_connection())会这样做?

编辑:

进一步揭开神秘面纱 - 如果我在 metadata.create_all(engine)之后创建连接,它就可以了!嗯,有点。

我可以从表中进行选择,但是当我得到engine.raw_connection()时它会在.copy_expert上失败,因为它无法找到该表。

2 个答案:

答案 0 :(得分:3)

首先要注意的是临时表只对创建它们的连接可见。

第二个是Engine没有封装单个连接;它管理connection pool

最后,documentation指出直接在Engine(在他们的示例中为engine.execute("select ..."))执行的操作将在内部获取并释放自己的连接。

考虑到所有这一切,很清楚你的例子中发生了什么:

  • conn = engine.connect()从池中获取Connection#1。
  • metadata.create_all(engine)隐式获取Connection#2(因为引擎的角度来看,#1仍在“正在使用中”),使用它来创建表,然后将其释放回池中。
  • pg_conn = engine.raw_connection()再次获得#2,因此通过此对象执行的COPY仍然可以看到该表。
  • conn仍在使用#1,您通过此对象执行的任何操作都无法查看您的临时表。

在你的第二个案例中:

  • metadata.create_all(engine)隐式获取/使用/发布Connection#1。
  • conn = engine.connect()获得#1并持有它。
  • pg_conn = engine.raw_connection()获取#2,COPY无法找到临时表。

故事的寓意:如果你正在做一些依赖于连接状态的事情,你最好确定你正在使用哪种连接。直接在engine上运行命令对于独立操作来说很好,但是对于涉及临时表的任何事情,你应该获得一个连接并坚持每一步(包括创建表,我建议你改为{{1 }})。

答案 1 :(得分:0)

嗯,这不会回答为什么,但它是如何实现我想要的。

而不是:

pg_conn = engine.raw_connection()

with pg_conn.cursor() as cursor:

只需将其替换为:

with conn.connection.cursor() as cursor:

SQLAlchemy连接对象exposes its underlying DBAPI connection通过.connection属性。无论涉及什么魔法都是正确的。