繁体   English   中英

如何编写 Postgres Dynamic SQL 语句来评估存储在行中的表达式?

[英]How to write Postgres Dynamic SQL statement to evaluate expressions stored in rows?

我正在使用 PostgreSQL,我需要编写 SQL 语句来解决这两个表的以下问题:

                                  Table "Transaction"
    Column      |              Type              | Collation | Nullable |      Default
----------------+--------------------------------+-----------+----------+-------------------
 id             | text                           |           | not null |
 amount         | integer                        |           | not null |
 merchant_name  | text                           |           | not null |

                                 Table "Rule"
     Column     |              Type              | Collation | Nullable |      Default
----------------+--------------------------------+-----------+----------+-------------------
 id             | text                           |           | not null |
 key            | text                           |           | not null |
 sql_expression | text                           |           | not null |

例如,有 2 个规则行:

  id  |       key       |       sql_expression
------+-----------------+-----------------------
   1  | amount          | > 100
   2  | merchant_name   | ~* '.*amazon.*'

给定一个事务 id(让我们称匹配的事务行"this_transaction" ),我想找到所有规则行,其中每个规则行的键和 sql_expression 值从 "this_transaction" 构造的条件计算为真。 在这个例子中,如果"this_transaction".amount > 100 ,我想要规则 1,如果"this_transaction".merchant_name ~* '.*amazon.*'我想要规则 2

我不知道如何写这个 WHERE 表达式。 下面提供了一个无效的 SQL 伪代码。

SELECT "Rule".* FROM "Rule",
  (
    SELECT "Transaction".*
    FROM "Transaction"
    WHERE "Transaction".id = $1
  ) AS "this_transaction"
WHERE "this_transaction".("Rule".key) "Rule".sql_expression;

$1 = 'some_transaction_id'

如果有人对如何编写 WHERE 表达式或以其他方式解决问题有任何建议,请告诉我。 感谢您的时间和考虑。

PS 在我们的情况下,我们将有许多规则行,这些行会被许多用户不断创建、更新和删除。 请提出一个支持这个灵活系统的解决方案。 谢谢你。

create or replace function eval_bool(data record, key text, expr text)
returns bool stable strict language plpgsql
as $$
declare
    result bool;
begin
    execute 'select $1.' || key || ' ' || expr into result using data;
    return result;
end $$;

-- test:

select relname
from pg_class as t
where eval_bool(t, 'relname', 'like ''%class%''');

┌───────────────────────────────────┐
│              relname              │
├───────────────────────────────────┤
│ pg_class_oid_index                │
│ pg_class_relname_nsp_index        │
│ pg_class_tblspc_relfilenode_index │
│ pg_opclass_am_name_nsp_index      │
│ pg_opclass_oid_index              │
│ pg_class                          │
│ pg_opclass                        │
└───────────────────────────────────┘

现在应该很简单:

SELECT "Rule".* FROM "Rule",
  (
    SELECT "Transaction".*
    FROM "Transaction"
    WHERE "Transaction".id = $1
  ) AS "this_transaction"
WHERE eval_bool("this_transaction"::"Transaction", "Rule".key, "Rule".sql_expression);

请注意,第一个值( data )应该是注册类型的值。 所以我们需要将子查询结果转换为"Transaction" type

那是棘手的地形。

您可以编写一个返回 function 的集合,该集合采用事务 ID,迭代规则并在具有给定 ID 的事务和规则表达式存在时返回规则。

CREATE FUNCTION transaction_rule
                (_transaction transaction.id%TYPE)
                RETURNS SETOF rule
AS
$$
DECLARE
  _exists boolean;
  _rule rule;
BEGIN
  FOR _rule IN (SELECT *
                       FROM rule) LOOP
    EXECUTE format(concat('SELECT EXISTS (SELECT *',
                          '                      FROM transaction',
                          '                      WHERE id = $1',
                          '                      AND %I %s);'),
                   _rule.key,
                   _rule.sql_expression)
            USING _transaction
            INTO _exists;
    IF _exists THEN
      RETURN NEXT _rule;
    END IF;
  END LOOP;  
END;
$$
LANGUAGE plpgsql;

但这大概会表现得很糟糕。 这很容易出错,每个可以更改规则的人都可以滥用它来进行 SQL 注入攻击。

如果您需要将规则存储在数据库中而不是应用程序中,也许您可以使用事务视图并将规则添加为列。 类似于以下内容:

CREATE VIEW transaction_rules
AS
SELECT t.*,
       amount > 100 rule_1,
       merchant_name ~* '.*amazon.*' rule_2
       FROM transaction t;

db<>小提琴

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM