如何限制存储过程中的任何 DML 操作

Posted

技术标签:

【中文标题】如何限制存储过程中的任何 DML 操作【英文标题】:How to restrict any DML operation in a stored procedure 【发布时间】:2021-03-21 15:08:51 【问题描述】:

我正在尝试将特定月份的一些记录插入到表中。如何在存储过程中的其他月份限制该表上的任何 DML 操作(没有任何触发器或约束)。请帮助我,在此先感谢。

【问题讨论】:

请edit 与minimal reproducible example 联系您的问题,包括:您正在使用的程序示例;您的最终用户将如何调用该过程的示例;您的表格的详细信息。 【参考方案1】:

将“特定月份”作为参数传递并在整个过程的代码中使用它,很可能在WHERE 子句或IFs、CASEs 等中使用。

【讨论】:

我们可以通过它,但我们如何限制最终用户何时将插入或更新到表中的任何其他月份。 创建由您维护的附加表,其中仅包含允许的月份。从过程中引用该表。【参考方案2】:

创建 2 个用户:

第一个(我们可以称之为DATA_OWNER)将拥有包含数据的表以及在这些表上执行 DML 的存储过程/包;和 第二个(我们可以称之为END_USER)将是您的最终用户连接的数据库用户。

然后您可以创建表:

CREATE TABLE data_owner.table_name (
  id       NUMBER
           GENERATED ALWAYS AS IDENTITY
           PRIMARY KEY,
  datetime DATE
           NOT NULL,
  value    NUMBER
);

CREATE TABLE data_owner.table_name_insert_bounds (
  start_datetime DATE,
  end_datetime DATE,
  CONSTRAINT table_name_insert_bounds__pk PRIMARY KEY ( start_datetime, end_datetime ),
  CONSTRAINT table_name_insert_bounds__chk CHECK ( start_datetime <= end_datetime )
);

还有一个包含您的存储过程的包:

CREATE PACKAGE data_owner.table_name_management_pkg IS
  PROCEDURE add_value(
    i_datetime IN  TABLE_NAME.DATETIME%TYPE,
    i_value    IN  TABLE_NAME.VALUE%TYPE
  );
END;
/

CREATE PACKAGE BODY data_owner.table_name_management_pkg IS
  PROCEDURE add_value(
    i_datetime IN  TABLE_NAME.DATETIME%TYPE,
    i_value    IN  TABLE_NAME.VALUE%TYPE
  )
  IS
    valid_datetime NUMBER;
  BEGIN
    SELECT CASE
           WHEN EXISTS(
                  SELECT 1
                  FROM   table_name_insert_bounds
                  WHERE  i_datetime BETWEEN start_datetime AND end_datetime
                )
           THEN 1
           ELSE 0
           END
    INTO   valid_datetime
    FROM   DUAL;
    
    IF valid_datetime = 0 THEN
      RAISE_APPLICATION_ERROR(
        -20000,
        'Date-time is outside of current insertion range.'
      );
    END IF;
    
    INSERT INTO table_name ( datetime, value )
    VALUES ( i_datetime, i_value );
  END;
END;
/

然后将权限授予最终用户:

GRANT SELECT ON data_owner.table_name TO end_user;
GRANT EXECUTE ON data_owner.table_name_management_pkg TO end_user;

那么最终用户只能通过包中的存储过程执行DML操作(他们无权绕过过程直接对表执行DML)但也可以SELECT所有数据和数据所有者可以设置最终用户能够插入的日期范围(并且最终用户无法修改这些范围)。

例如:

如果数据所有者设置了这些界限:

INSERT INTO table_name_insert_bounds ( start_datetime, end_datetime )
VALUES ( DATE '2020-01-01', DATE '2020-02-01' );

那么最终用户就可以运行了:

BEGIN
  data_owner.table_name_management_pkg.ADD_VALUE( DATE '2020-01-10', 42 );
END;
/

将插入一行,但如果他们尝试:

BEGIN
  data_owner.table_name_management_pkg.ADD_VALUE( DATE '2020-02-02', 13 );
END;
/

然后会得到异常:

ORA-20000: Date-time is outside of current insertion range.

db小提琴here

【讨论】:

以上是关于如何限制存储过程中的任何 DML 操作的主要内容,如果未能解决你的问题,请参考以下文章

为我的模式中的所有表自动生成 DML 存储过程的工具-MySQL

Oracle数据库中有关触发器问题

oracle中在编写存储过程启动多线程的问题?

SQL语句(二十一)—— 触发器(DML触发器)

存储过程和函数的区别

在存储过程中对 DML 语句使用 EXECUTE IMMEDIATE