【问题标题】:mapping psql type oids to python types - with psycopg2将 psql 类型 oid 映射到 python 类型 - 使用 psycopg2
【发布时间】:2023-03-20 10:36:01
【问题描述】:

这是故事。

我有一堆存储过程,并且都有自己的参数类型。
我要做的是在 python 中创建一个类型安全层,这样我就可以在访问数据库之前确保所有值的类型都是正确的。

当然我不想在 python 中再次编写整个架构,所以我想我可以通过从数据库中获取参数名称和类型在启动时自动生成此信息。

所以我继续破解这个查询只是为了测试

SELECT proname, proargnames, proargtypes 
FROM pg_catalog.pg_namespace n
JOIN pg_catalog.pg_proc p
ON pronamespace = n.oid
WHERE nspname = 'public';

然后我从 python 运行它,对于“proargtypes”,我为每个结果得到一个这样的字符串

'1043 23 1043'

我敏锐的眼睛告诉我,这些是 postgresql 类型的 oid,用空格分隔,这个特殊的字符串意味着函数接受 varchar、integer、varchar。 所以用python说话,这应该是

(unicode, int, unicode)

现在如何从这些数字中获取 python 类型?

理想的最终结果应该是这样的

In [129]: get_python_type(23)
Out[129]: int

我查看了 psycopg2 的所有内容,发现最接近的是“extensions.string_types”,但这只是将 oid 映射到 sql 类型名称。

【问题讨论】:

    标签: python postgresql psycopg2


    【解决方案1】:

    如果您想要 Python 类型类,就像您可能从 SQLALchemy 列对象中获得的那样,您需要构建和维护自己的映射。 psycopg2 没有,即使在内部也是如此。

    但是,如果您想要的是一种从 oid 获取将原始值转换为 Python 实例的函数的方法,那么 psycopg2.extensions.string_types 实际上已经是您所需要的。它可能看起来只是从oid 到名称的映射,但事实并非如此:它的值不是字符串,它们是psycopg2._psycopg.type 的实例。是时候深入研究一些代码了。

    psycopg2 公开了一个用于注册新类型转换器的 API,我们可以使用它来追溯涉及类型转换的 C 代码;这以typecastObject 为中心 typecast.c,不出所料,它映射到我们在老朋友string_types 中找到的psycopg2._psycopg.type。这个对象包含两个函数的指针,pcast(用于 Python 转换函数)和ccast(用于 C 转换函数),这看起来就像我们想要的那样——只要选择存在的一个并调用它,问题就解决了。除了它们不在暴露的属性中(name,它只是一个标签,values,它是一个 oid 列表)。该类型向 Python 公开的是__call__,事实证明,它只是在pcastccast 之间进行选择。此方法的文档非常无用,但进一步查看 C 代码shows 发现它有两个参数:一个包含原始值的字符串和一个游标对象。

    >>> import psycopg2.extensions
    >>> cur = something_that_gets_a_cursor_object()
    >>> psycopg2.extensions.string_types[23]
    <psycopg2._psycopg.type 'INTEGER' at 0xDEADBEEF>
    >>> psycopg2.extensions.string_types[23]('100', cur)
    100
    >>> psycopg2.extensions.string_types[23]('10.0', cur)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: invalid literal for int() with base 10: '10.0'
    >>> string_types[1114]
    <psycopg2._psycopg.type 'DATETIME' at 0xDEADBEEF>
    >>> string_types[1114]('2018-11-15 21:35:21', cur)
    datetime.datetime(2018, 11, 15, 21, 35, 21)
    

    不幸的是,需要一个游标,事实上,并不总是需要一个游标:

    >>> string_types[23]('100', None)
    100
    

    但是任何必须转换实际字符串(例如varchar)类型的事情都取决于 PostgreSQL 服务器的语言环境,至少在 Python 3 编译中,并且将None 传递给这些施法者不仅会失败——它段错误。您提到的方法cursor.cast(oid, raw) 本质上是psycopg2.extensions.string_types 中脚轮的包装,在某些情况下可能更方便。

    我能想到的需要光标和连接的唯一解决方法是构建一个模拟连接对象。如果它在没有连接到实际数据库的情况下暴露了所有相关的环境信息,它可以附加到一个游标对象并与string_types[oid](raw, cur)cur.cast(oid, raw) 一起使用,但该模拟将在 C 中构建并保留为给读者练习。

    【讨论】:

      【解决方案2】:

      postgres 类型和 python 类型的映射给了here。这有帮助吗?

      编辑: 当您从表中读取记录时,postgres(或任何数据库)驱动程序会自动将记录列类型映射到 Python 类型。

      cur = con.cursor()
      cur.execute("SELECT * FROM Writers")
      
      row = cur.fetchone()
      
      for index, val in enumerate(row):
          print "column {0} value {1} of type {2}".format(index, val, type(val))
      

      现在,您只需在编写 MySQL 接口代码时将 Python 类型映射到 MySQL 类型。 但是,坦率地说,这是将类型从 PostgreSQL 类型映射到 MySQL 类型的一种迂回方式。我只想参考这两个数据库之间的众多类型映射之一,例如this

      【讨论】:

      • 我正在了解如何注册新类型以及如何将值从 python 类型转换为 sql 等等,但是说“23 变为 int”或“1043 变为 str”的代码到底在哪里?
      • 您的编辑将起作用,除非任何列具有空值,然后类型将变为 None 并且所有内容都丢失了。此外,这些过程仅用于编写,它们的参数不直接映射到相应的视图。不过,我找到了“cursor.cast”方法,这是我可以使用的方法。
      • 我也有这个问题,但是编辑不起作用,因为不能保证 fetch one 实际上返回任何东西(例如,如果数据库中没有记录)。然而,即使在那种情况下,我仍然可以通过调用 cursor.description 来获取列类型 OID,但我想知道它们被翻译成什么 python 类型。显然信息在某处可用,因为 psycopg 进行转换,问题是如何访问这些信息。
      猜你喜欢
      • 2020-05-17
      • 1970-01-01
      • 1970-01-01
      • 2016-04-15
      • 2015-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多