【发布时间】:2021-09-14 04:37:47
【问题描述】:
我正在尝试从 plpgsql 函数中的 QUERY EXEUTE 获取返回,以便能够检查动态更新查询影响了多少行。我的用例是在插入或更新到动态设置的表时将事件(带有自定义有效负载)添加到单独的表中。因为我的事件有一个自定义有效负载,所以我无法使用数据库触发器(例如,插入前触发器)。作为一个简化的例子,假设我有这个表:
CREATE TABLE users (user_id text primary key, name text)
这是我的简化事件表:
CREATE TABLE events(event_id text primary key, payload json)
这是我的简化函数:
CREATE OR REPLACE FUNCTION my_function(_rowtype anyelement, q text, payload jsonb)
RETURNS SETOF anyelement AS
$func$
DECLARE
event_id text;
BEGIN
SELECT jsonb_object_field_text (payload, 'id')::text INTO STRICT event_id;
execute format('insert into event(event_id, payload) values ($1, $2)') using event_id, payload;
RETURN QUERY EXECUTE format('%s', q);
END
$func$ LANGUAGE plpgsql;
我们的目标是让这项工作与有人在交易中创建这些工作完全相同。在插入的伪代码中:
BEGIN
insert into events(id, payload) values($1, $2)
insert into users(columns) values(<any values>)
COMMIT
同样的更新:
BEGIN
insert into events(id, payload) values($1, $2)
result, error := query(`update users set name = 'hello' where id = 'Not Exists Thus No Rows Modified'`);
if result.rowsAffected() == 0 {
ROLLBACK
}
COMMIT
函数my_function 几乎可以工作,除了一个边缘情况:当更新实际上不影响任何行时。
例如,这有效:
select * from my_function(NULL::users,
'insert into users(id,name) values('u1', ''a2'') returning *',
payload => '{"id": "e1", "custom": "s1", "field": "2019-10-12T07:20:50.52Z"}')
正如预期的那样,完成此操作后,用户表中的一行和事件表都被创建了。 失败的情况如下:
select * from my_function(NULL::users,
'update users set name = ''hello'' where user_id = ''NotExists'' returning *',
payload => '{"id": "e2", "custom": "s3", "field": "2019-10-12T07:20:50.52Z"}')
在这里,在 events 表中创建了一行(我的目标是不应该创建它)。
我知道这种方法并不优雅,而且我知道这很容易受到 SQL 注入的影响。我很想提出更好的方法来解决这个问题(包括取消我们现在正在做的事情)。但要直接回答这个问题,我希望存储 QUERY EXECUTE 的结果,检查是否有任何行受到影响,并引发错误,这样就不会出现在事件表中创建行的情况用户表中没有真正的相应变化。用户表只是一个例子,一般来说,它可以是任何动态设置的表。
【问题讨论】:
-
非常感谢 a_horse_with_no_name!我非常感谢你的评论。我在那里看到我可能可以使用
FOUND但是,我不知道如何存储结果,因为返回的类型是动态的。如,我不能做这样的事情:``` EXECUTE format('%s', q) into row_with_dynamic_type; IF NOT FOUND THEN RAISE EXCEPTION '失败' END IF; RETURN row_with_dynamic_type ``` 因为执行可能会根据动态查询返回一列或多列。你有什么建议吗?我可以将什么设置为row_with_dynamic_type的类型以便我仍然可以返回? -
不,不是
FOUND变量,而是GET DIAGNOSTICS integer_var = ROW_COUNT;构造将通过检查integer_var的值来帮助解决您的问题。 -
谢谢Stevanov.sm!我仍然有同样的问题,那就是如何将
QUERY EXECUTE的输出存储到我不知道类型的变量中(以便能够使用GET DIAGNOSTICS。我怎样才能让QUERY EXECUTE format('%s', q) into <what goes here?>跟随那是GET DIAGNOSTICS integer_var = ROW_COUNT;,最后是RETURN <what goes here?>? -
jsonb_object_field_text()不是 Postgres 函数。你能透露一下它的定义吗?不祥的q从何而来?我之所以问,是因为如果我见过的话,这是 SQL 注入的网关。请始终公开您的 Postgres 版本。
标签: postgresql triggers plpgsql dynamic-sql