我正在try 使用Python(3.11)和oracledb库(1.3.0)从Oracle数据库中获取SDO_GEOMETRY类型的列.我想使用outputtypeHandler将SDO_GEOMETRY实例转换为PICKLE编码的字节.如果我try 将cursor.var中的typ参数设置为typ=str,这对于数字列很有效,但对于所有类型的列类型,这对于typ=bytestyp=oracledb.DB_TYPE_RAW都失败了.无论参数值为typ,SDO_GEOMETRY列始终会产生错误.输出转换器甚至不会被调用,如下所示.

以下是我的示例代码:

import oracledb
import pickle


def output_type_handler(cursor, name, default_type, size, precision, scale):

    def pickle_converter(obj) -> bytes:
        print(f"Converter called for {name}.")
        return pickle.dumps(obj)

    if default_type == oracledb.DB_TYPE_OBJECT:
        return cursor.var(
            typ=oracledb.DB_TYPE_RAW, 
            size=size, 
            arraysize=cursor.arraysize, 
            outconverter=pickle_converter
        )

# Switch to thick mode
oracledb.init_oracle_client()

ora_connection = oracledb.connect(
    dsn=oracledb.makedsn("ora.local", 1521, "TST"),
    user="test",
    password="test"
)

ora_connection.outputtypehandler = output_type_handler

with ora_connection.cursor() as cursor:
    # GEOMETRIE is an SDO_GEOMETRY column
    recs = cursor.execute("SELECT GEOMETRIE FROM MV_CS_STWG1KP").fetchmany(5)
    print(recs)

OUTPUT(请注意,行Converter called for ...甚至没有打印,因此从未调用转换器):

Traceback (most recent call last):
  File "/home/jannis/.config/JetBrains/PyCharmCE2023.1/scratches/tmp.py", line 28, in <module>
    num_recs = cursor.execute("SELECT GEOMETRIE FROM MV_CS_STWG1KP").fetchmany(5)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jannis/PycharmProjects/etl_engine/venv/lib/python3.11/site-packages/oracledb/cursor.py", line 492, in fetchmany
    row = fetch_next_row(self)
          ^^^^^^^^^^^^^^^^^^^^
  File "src/oracledb/impl/base/cursor.pyx", line 397, in oracledb.base_impl.BaseCursorImpl.fetch_next_row
  File "src/oracledb/impl/thick/cursor.pyx", line 132, in oracledb.thick_impl.ThickCursorImpl._fetch_rows
  File "src/oracledb/impl/thick/utils.pyx", line 413, in oracledb.thick_impl._raise_from_odpi
  File "src/oracledb/impl/thick/utils.pyx", line 403, in oracledb.thick_impl._raise_from_info
oracledb.exceptions.DatabaseError: ORA-00932: inconsistent datatypes: expected BINARY got ADT

我必须使用胖模式才能连接到较旧的Oracle数据库. 我怎么才能解决这个问题?

推荐答案

在序列化它之前,您需要将其转换为一个Python对象.即使删除输出处理程序并显式地酸洗也会产生错误:

cur.execute("select geometry from testgeometry")
r, = cur.fetchone()
p = pickle.dumps(r) # fails with error "KeyError: '__getstate__'"

相反,可以try 以下方法.它使用类型转换器将其转换为一个Python对象,然后使用行工厂对其进行处理.

class mySDO(object):
    def __init__(self, gtype, elemInfo, ordinates):
        self.gtype = gtype
        self.elemInfo = elemInfo
        self.ordinates = ordinates

obj_type = con.gettype("MDSYS.SDO_GEOMETRY")

def SDOOutputTypeHandler(cursor, name, default_type, size, precision, scale):
    def SDOOutConverter(DBobj):
        return mySDO(int(DBobj.SDO_GTYPE), DBobj.SDO_ELEM_INFO.aslist(), DBobj.SDO_ORDINATES.aslist())

    if default_type == oracledb.DB_TYPE_OBJECT:
        return cursor.var(obj_type, arraysize=cursor.arraysize, outconverter=SDOOutConverter)

cur.outputtypehandler = SDOOutputTypeHandler

cur.execute("select geometry from testgeometry")
cur.rowfactory = lambda *args: pickle.dumps(args)
p = cur.fetchone()
print(p)

然而,对空间对象使用‘熟知二进制’(WKB)格式会更好吗?您可以直接从DB获得,而不需要输出转换器或行工厂:

oracledb.defaults.fetch_lobs = False

cur = con.cursor()
cur.execute("select sdo_util.to_wkbgeometry(geometry) from testgeometry")
b = cur.fetchone()
print(b)

Python相关问答推荐

解析讨论论坛只给我第一个用户 comments ,但没有给我其他用户回复

如何使用函数正确索引收件箱?

在Arrow上迭代的快速方法.Julia中包含3000万行和25列的表

拆分pandas列并创建包含这些拆分值计数的新列

列表上值总和最多为K(以O(log n))的最大元素数

TARete错误:类型对象任务没有属性模型'

将特定列信息移动到当前行下的新行

try 在树叶 map 上应用覆盖磁贴

为什么带有dropna=False的groupby会阻止后续的MultiIndex.dropna()工作?

可变参数数量的重载类型(args或kwargs)

无法使用requests或Selenium抓取一个href链接

把一个pandas文件夹从juyter笔记本放到堆栈溢出问题中的最快方法?

NumPy中条件嵌套for循环的向量化

部分视图的DataFrame

如何在图中标记平均点?

Python列表不会在条件while循环中正确随机化'

为什么常规操作不以其就地对应操作为基础?

合并与拼接并举

python sklearn ValueError:使用序列设置数组元素

如何求相邻对序列中元素 Select 的最小代价