postgresql 如何使用pg8000将数组中的NULL传递给Postgres存储过程?

0g0grzrc  于 2023-06-05  发布在  PostgreSQL
关注(0)|答案(1)|浏览(113)

在Python 3.8中,我使用pg8000 1.21.3调用Postgress 11存储过程,该存储过程将自定义数据类型数组作为参数。下面是概念模式和存储过程的工作证明:

DROP PROCEDURE IF EXISTS myproc;
DROP TABLE IF EXISTS mytable;
DROP TYPE mytype;

CREATE TYPE mytype AS (
    an_int     INT,
    a_bool     BOOL,
    a_decimal  DECIMAL,
    a_string   TEXT
);

CREATE TABLE mytable(
    id         SERIAL PRIMARY KEY,
    an_int     INT,
    a_bool     BOOL,
    a_decimal  DECIMAL,
    a_string   TEXT
);

CREATE PROCEDURE myproc(
    IN myarray mytype[]
)
AS $$
BEGIN
        INSERT INTO mytable(
            an_int,
            a_bool,
            a_decimal,
            a_string
        ) SELECT
            an_int,
            a_bool,
            a_decimal,
            a_string
        FROM
            unnest(myarray);
    END;
$$
LANGUAGE 'plpgsql';

CALL myproc(
    array[
            (1, 1, 2.1, 'foo'),
            (NULL, NULL, NULL, NULL)
        ]::mytype[]
);

SELECT * FROM mytable;

select的输出是:

id | an_int | a_bool | a_decimal | a_string 
----+--------+--------+-----------+----------
  1 |      1 | t      |       2.1 | foo
  2 |        |        |           | 
(2 rows)

这意味着当我从SQL调用存储过程时,我可以创建一个全为空值的行。
我现在正在尝试使用pg8000从Python实现相同的功能。要传递一个包含一些null值的数组,唯一可行的方法是用一个空字符串替换None。这适用于整数和文本数据类型,但对于布尔和小数则失败:

import pg8000

arr = [(1, True, 3.2, 'foo'),
       (None, True, 3.2, 'foo'),
       (1, True, 3.2, None),
       (1, False, 3.2, 'foo'),
       (1, None, 3.2, 'foo'),  # <---- boolean null fails
        (1, False, None, 'foo'), # <---- decimal null fails
       ]

sql = """CALL myproc(%s::mytype[]);"""
stm = [str(item) for item in arr]
stm = [item.replace('None', '') for item in stm]

with pg8000.connect(host="localhost", database="mydb", user="myuser", password="mypass") as con:
    with con.cursor() as cursor:
        cursor.execute(
            sql,
            args=(stm,)
        )
    con.commit()

误差为:
pg8000.dbapi.ProgrammingError: {'S': 'ERROR', 'V': 'ERROR', 'C': '22P02', 'M': 'invalid input syntax for type boolean: " "', 'F': 'bool.c', 'L': '154', 'R': 'boolin'}
如何使用pg8000参数插值为所有这些数据类型传递空值,也就是说,不对sql语句进行硬编码?

dnph8jn4

dnph8jn41#

使用pg8000的1.29.6版本,您应该能够:

import pg8000

arr = [
    (1, True, 3.2, "foo"),
    (None, True, 3.2, "foo"),
    (1, True, 3.2, None),
    (1, False, 3.2, "foo"),
    (1, None, 3.2, "foo"),  # <---- boolean null fails
    (1, False, None, "foo"),  # <---- decimal null fails
]

sql = """CALL myproc(%s::mytype[]);"""

with pg8000.connect(database="typetest", user="postgres", password="pg") as con:
    with con.cursor() as cursor:
        cursor.execute(sql, args=(arr,))
    con.commit()

因为pg8000现在将tuple解释为复合类型。

相关问题