SQL Alchemy 自定义类型,强制绑定 BLOB 参数

0 投票
1 回答
72 浏览
提问于 2025-04-12 04:33

我正在尝试为Oracle数据库在SQL Alchemy中添加基本的几何支持,因为Geoalchemy2不支持这一点。我创建了一个自定义数据类型,如果几何图形不太大,它工作得很好。但是,由于我使用的是wkb或wkt作为输入,当几何图形的坐标数量很大时,就会超出varchar2的限制。

(oracledb.exceptions.DatabaseError) ORA-01461: 绑定位置2的值超出了最大VARCHAR2长度。

我想强制它使用LOB作为输入,但似乎无法做到。或者有没有其他简单的方法来解决这个问题……最好是不用麻烦地处理Oracle的几何类型。

这是我目前的代码:

from shapely import from_wkt, Point, wkb
from sqlalchemy import types, Integer, String, create_engine, Column, func
from sqlalchemy.orm import declarative_base, Session
Base = declarative_base()
metadata = Base.metadata


class OracleGeometry(types.UserDefinedType):
    cache_ok = True


    def __init__(self, srid=4326):
        self.srid = srid

    def get_col_spec(self, **kw):
        return "SDO_GEOMETRY"

    def bind_expression(self, bindvalue: Point):
        # adding the nvl2 to make sure it doesn't crash if a null value is added
        return func.nvl2(bindvalue, func.sdo_util.FROM_WKBGEOMETRY(bindvalue), None)

    def column_expression(self, col):
        return func.sdo_util.TO_WKTGEOMETRY(col, type_=self)

    def bind_processor(self, dialect):
        def process(value):
            if value is None:
                return None
            return value.wkb_hex
        return process


    def result_processor(self, dialect, coltype):
        def process(value):
            if value is None:
                return None
            return from_wkt(value)

        return process


class Test2(Base):
    __tablename__ = 'test2'

    id = Column(Integer, primary_key=True)
    test = Column(OracleGeometry(4326))


engine = create_engine(
    f"oracle+oracledb://belmap:belmap@localhost:1521?service_name=FREE", echo=True)

# Base.metadata.create_all(engine)
with Session(engine) as session:
    session.query(Test2).delete()
    wkt = 'MULTIPOLYGON(((....' # Geometry exceeding 4000 characters in wkb or wkt
    t = Test2(id=123, test=from_wkt(wkt))
    t2 = Test2(id=12345, test=None) # Always testing to see if null values work
    session.add_all([t])
    for x in session.query(Test2).all():
        print(f"{x.id} {x.test}")
    session.commit(

)

顺便说一下,我使用的是:

python 3.12

sqlalchemy 2.0.29

oracledb 2.0.1

shapely 2.0.2

编辑

所以,我决定尝试Oracle的几何定义。现在我的自定义类型看起来像这样(对于多边形):

class OracleGeometry(types.UserDefinedType):
    cache_ok = True

    def __init__(self, srid=4326):
        self.srid = srid

    def get_col_spec(self, **kw):
        return "SDO_GEOMETRY"

    def bind_expression(self, bindvalue: Optional[List]):
        # return bindvalue
        return func.nvl2(bindvalue, func.sdo_geometry(bindvalue), None)

    def column_expression(self, col):
        return func.sdo_util.TO_WKTGEOMETRY(col, type_=self)

    def bind_processor(self, dialect):
        def process(geom):
            if geom is None:
                return None
            # return BindParameter(value=value.wkb_hex, type_=BLOB)#str(value.wkb_hex).encode()
            # return value.wkb_hex.encode()
            sdo_gtype = 2007
            sdo_point = None
            sdo_elem_array = []
            sdo_coordinates = []
            for g in geom.geoms:
                g: Polygon
                sdo_elem_array.extend([len(sdo_coordinates) + 1,1003,1])
                for c in g.exterior.coords:
                    sdo_coordinates.extend([c[0], c[1]])
                for i in g.interiors:
                    sdo_elem_array.extend([len(sdo_coordinates) + 1, 2003,1])
                    for c in i.coords:
                        sdo_coordinates.extend([c[0], c[1]])
            return [sdo_gtype, self.srid, sdo_point, sdo_elem_array, sdo_coordinates]
        return process


    def result_processor(self, dialect, coltype):
        def process(value):
            if value is None:
                return None
            return from_wkt(value)

        return process

但现在我遇到了错误:sqlalchemy.exc.NotSupportedError: (oracledb.exceptions.NotSupportedError) DPY-3002: 不支持类型为“list”的Python值。

1 个回答

暂无回答

撰写回答