【问题标题】:cx_Oracle.DatabaseError: DPI-1039: statement was already closedcx_Oracle.DatabaseError: DPI-1039: 语句已经关闭
【发布时间】:2019-10-19 22:44:40
【问题描述】:

升级到最新的 Cx_Oracle 版本(5.1.3 到 7.1.3),现在我所有的代码都坏了。尝试传递光标时似乎出现问题。

def select_query_remy(self,sql,params=None):
    with cx_Oracle.connect(self.connection) as con:
        cur = con.cursor()
        if params:
            cur.execute(sql,params)
        else:
            cur.execute(sql)
        #print cur.fetchall()
        return cur

我在 fetchall 中获得了正确的数据,因此我知道它连接正确并返回了有效结果。所以我把它注释掉。

在我调用它的其他脚本中,行现在应该正确存储光标?

rows = con.select_query_remy(sql)

当我从另一个脚本中fetchall() 时,我得到下面并得到错误。

print rows.fetchall()

cx_Oracle.DatabaseError: DPI-1039: 语句已关闭

有什么想法吗?

完整的 db.py

import cx_Oracle

class db:

list ={}    

def __init__(self):
    '''
        AMFM Python DB connectors
   '''
    db.list["disprod"] = [999,"disprod.prod.com"]
    db.list["prodqry"] = [999,"prodqry.prod.com"]
    db.list["amfmprod"] = [999,"amfmprod.prod.com"]
    db.list["esriprod"] = [999,"esriprod.prod.com"]
    db.list["amfmtest"] = [999,"amfmtest.prod.com"]
    db.list["amfmdev"] = [999,"CCTdAMFM.prod.com"]
    db.list["amfmtran"] = [999,"amfmtran.prod.com"]


def setup(self,env, username, password):
    self.username = username
    self.password = password
    self.env = env

    self.port = db.list[env][0]
    self.ip = db.list[env][1]
    if env == "prodqry":
        self.env = "prodqry.world"
    elif env == "amfmprod":
        self.env = "amfmprod.world"
    elif env == "esriprod":
        self.env = "esriprod.world"
    else:
        self.env = env
    self.connection = self.username+"/"+self.password+"@"+self.ip+":"+str(self.port)+"/"+self.env

def select_query_remy(self,sql,params=None):
    with cx_Oracle.connect(self.connection) as con:
        cur = None
        cur = con.cursor()
        if params:
            cur.execute(sql,params)
        else:
            cur.execute(sql)
        return cur

在另一个 python 中,我创建了一个新数据库并尝试恢复光标。

con = db()
con.setup("xxx","user","password")
rows = con.select_query_remy(sql)
print rows.fetchall()

结果:

Traceback (most recent call last):
  File "C:\Python27\ArcGIS10.5\test\test.py", line 30, in <module>
    print rows.fetchall()
cx_Oracle.DatabaseError: DPI-1039: statement was already closed

如果我返回 fetchall() 数据就会出现。当我通过光标本身时,就会发生错误。

def select_query_remy(self,sql,params=None):
        with cx_Oracle.connect(self.connection) as con:
            cur = None
            cur = con.cursor()
            if params:
                cur.execute(sql,params)
            else:
                cur.execute(sql)
            #return cur
            return cur.fetchall()

任何想法为什么我不能像过去那样传递光标?

【问题讨论】:

    标签: python python-2.7 cx-oracle


    【解决方案1】:

    您的错误与with-block(也称为上下文管理器)有关。让我在select_query_remy 函数中添加几个 cmets:

    def select_query_remy(self,sql,params=None):
        with cx_Oracle.connect(self.connection) as con:  # connects to database
            cur = None
            cur = con.cursor()
            if params:
                cur.execute(sql,params)
            else:
                cur.execute(sql)
            return cur                                # closes connection (invalidates cursor too!)    
    

    由于with-block 管理数据库连接的上下文,所以在退出block 后连接会被关闭。

    要修复错误,我建议直接在函数内部执行fetchall,然后在函数外部处理该数据。 (你已经有点想通了。)

    延伸阅读:The Python Language Reference: The with statement

    【讨论】:

    • 这是新的吗?它以前没有这样的功能。
    • @Remy 不太清楚从 5.1.3 到 7.1.3 是什么通过 cx_Oracle (我不熟悉 cx_Oracle 模块)......可能是植根于实现的东西。通常,将光标限制在特定块上,并且不要通过从函数返回它来使其松散。 (另请参阅:Single Responsibility Principle)一些数据库模块还允许with-带有游标的块,在离开块时自动关闭它们。如果 cx_Oracle 实现了就使用它。
    • 你太棒了!!!!正如你所说。删除了 with ,它的功能与过去一样。
    • 会随着我的进步而使用它,但我会从其他人停止的地方继续。一切都以这种方式配置和编码。很多工作要改变,但最终会完成。再次感谢。
    • 来自release notes:“当连接用作上下文管理器时,连接现在在块结束时关闭。现在忽略设置 cx_Oracle.__future__.ctx_mgr_close 的尝试。”跨度>
    【解决方案2】:

    我在查询 Oracle 数据库时遇到此错误消息。我相信如果数据库中有 LOB(大对象)就会发生这种情况,而我查询过的所有 Oracle 数据库中都没有这种情况。

    由于我使用 Jupyter 进行原型设计,因此我更喜欢使用上下文管理器 (with) 语句,如果我必须在单独的单元格中添加 .close() 语句,这会使操作查询结果变得更加困难。

    我的解决方法实际上是使用内置的deepcopy 函数(我尝试使用.copy(),但这也不起作用。对数据的任何进一步操作/显示都会产生相同的错误。

    所以我给你的解决方案如下:

    from copy import deepcopy
    import cx_Oracle
    
    def select_query_remy(self,sql,params=None):
        with cx_Oracle.connect(self.connection) as con:
            cur = con.cursor()
            if params:
                cur.execute(sql,params)
            else:
                cur.execute(sql)
            #print cur.fetchall()
            return deepcopy(cur.fetchall())
    

    我相信如果它与我的问题相同,这应该可以解决您的问题。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-14
    • 2020-05-21
    • 2013-04-26
    • 1970-01-01
    • 2016-06-26
    • 2016-07-18
    • 1970-01-01
    相关资源
    最近更新 更多