我正在尝试使用 Python (3.11) 和 oracledb 库 (1.3.0) 从 Oracle 数据库中获取 SDO_GEOMETRY 类型的列。我想使用 outputtypehandler 将 SDO_GEOMETRY 实例转换为 pickle 编码字节。如果我尝试将
typ
中的 cursor.var
参数设置为 typ=str
,这对于 NUMBER 列工作正常,但对于各种列类型的 typ=bytes
和 typ=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)
输出(注意,
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__'"
而是尝试以下操作。它使用类型转换器转换为 Python 对象,然后使用行工厂对其进行 pickle。
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)格式会更好吗?您可以直接从数据库中获取它,而无需输出转换器或行工厂:
oracledb.defaults.fetch_lobs = False
cur = con.cursor()
cur.execute("select sdo_util.to_wkbgeometry(geometry) from testgeometry")
b = cur.fetchone()
print(b)