【问题标题】:Can Firebird SQL procedure know the parent procedure/trigger from which it is called from?Firebird SQL 过程可以知道调用它的父过程/触发器吗?
【发布时间】:2021-07-05 01:05:40
【问题描述】:

我有一个 SQL 过程,如果它是从一个特定过程调用的,它应该返回一些不同的结果。 SQL 过程是否有可能检测到它是从一个特定的其他 SQL 过程调用的?

或许监控mon$...表数据可以给出答案?

适用于 Firebird 2.1 的问题

例如有 mon$call_stack 表,但对于 Firebird 2.1,大多数情况下 mon$... 表是空的,它们会为更高版本的 Firebird 填满。

【问题讨论】:

    标签: firebird firebird2.1


    【解决方案1】:

    我不知道有任何此类选项。如果您的程序在从特定程序调用时应该表现出特殊行为,我建议您通过添加一个指定行为类型的额外参数或将其分成两个不同的程序来使其明确。

    这样,你也可以直接测试行为。

    【讨论】:

      【解决方案2】:

      隐藏的数据依赖是个坏主意。程序员将“纯函数”视为一件好事是有原因的。也许不是在所有情况下,也不是不惜一切代价,但当其他因素不受影响时,最好是这样。

      https://en.wikipedia.org/wiki/Pure_function

      所以,Mark 是正确的,如果有什么东西会影响您的过程逻辑 - 那么最好通过成为显式函数参数来显式记录它。 除非你的明确目标是创建一个隐藏的后门。

      然而,这意味着该过程的所有“客户端”,所有可以调用它的位置,也应该更改,并且应该在开发期间和客户端升级期间协同完成部署站点。这可能很复杂。

      所以我宁愿建议创建一个新过程并将所有实际逻辑移入其中。

      https://en.wikipedia.org/wiki/Adapter_pattern

      假设你有一些

      create procedure old_proc(param1 type1, param2 type2, param3 type3) as 
      begin
         ....some real work and logic here....
      end;
      

      把它变成类似的东西

      create procedure new_proc(param1 type1, param2 type2, param3 type3, 
                  new_param smallint not null = 0) as 
      begin
         ....some real work and logic here....
         ....using new parameter for behavior fine-tuning...
      end;
      
      create procedure old_proc(param1 type1, param2 type2, param3 type3) as 
      begin
        execute procedure new_proc(param1, param2, param3)
      end;
      

      ...然后您明确地进行“一个特定过程”调用new_proc(...., 1)。然后逐渐地,一个接一个地,您会将所有程序从调用old_proc 转移到调用new_proc,最终当所有依赖项都转移到新的API 时,您将停用old_proc


      还有一个选项可以传递“隐藏的后门参数”——即上下文变量,在 Firebird 2.0 中引入

      https://www.firebirdsql.org/rlsnotesh/rlsnotes20.html#dml-dsql-context

      然后你的被调用者会这样检查

       .....normal execution
       if ( rdb$get_context('USER_TRANSACTION','my_caller') is not null) THEN BEGIN
            ....new behavior...
       end;
      

      但是,您必须在调用之前进行“一个特定的过程”以正确设置此变量(这很乏味但并不难)并在调用后正确删除它(这应该被正确地框起来,即使在任何错误/异常的情况下,这也很乏味且不容易)。

      【讨论】:

        【解决方案3】:

        虽然我同意最好的方法可能是在过程中添加一个参数以帮助识别从哪里调用它,但有时我们没有这样做的奢侈。考虑过程签名不能更改的场景,因为它位于遗留系统中并在许多地方调用。在这种情况下,我会考虑以下示例;

        在本例中,需要知道谁调用它的存储过程将被称为 SPROC_A。

        首先我们创建一个全局临时表

        CREATE GLOBAL TEMPORARY TABLE GTT_CALLING_PROC
           ( PKEY INTEGER primary key,
           CALLING_PROC VARCHAR(31))
           ON COMMIT DELETE ROWS;
        

        接下来我们创建另一个名为 SPROC_A_WRAPPER 的存储过程,它将包装对 SPROC_A 的调用

        CREATE OR ALTER PROCEDURE SPROC_A_WRAPPER
        AS
        DECLARE CALLING_SPROC VARCHAR(31);
        BEGIN
        
          DELETE FROM GTT_CALLING_PROC
          WHERE GTT_CALLING_PROC.PKEY = 1;
        
          INSERT INTO GTT_CALLING_PROC (
              PKEY,
              CALLING_PROC)
          VALUES (
              1,
              'SPROC_A_WRAPPPER');
        
          EXECUTE PROCEDURE SPROC_A;
        
          DELETE FROM GTT_CALLING_PROC
          WHERE GTT_CALLING_PROC.PKEY = 1;
        
        END
        

        最后我们有了 SPROC_A

        CREATE OR ALTER PROCEDURE SPROC_A
        AS
        DECLARE CALLING_SPROC VARCHAR(31);
        BEGIN
        
          SELECT FIRST 1 CALLING_PROC
          FROM GTT_CALLING_PROC
          WHERE GTT_CALLING_PROC.PKEY = 1
          INTO :CALLING_SPROC;
        
          IF (:CALLING_SPROC = 'SPROC_A_WRAPPER') THEN
          BEGIN
            /*  Do Something  */
          END
          ELSE
          BEGIN
            /*  Do Something Else */
          END
        END
        

        SPROC_A_WRAPPER 将填充 Temp 表,调用该 SPROC_A,然后从 Temp 表中删除该行,以防 SPROC_A 从同一事务中的其他位置调用,它不会认为 SPROC_A_WRAPPER 调用了它。

        虽然有些粗糙,但我相信这会满足您的需求。

        【讨论】:

        • Consider the scenario where the procedure signature can't change because it is in a legacy system and called in many places - 如果存在这种情况,那么您提出的SPROC_A_WRAPPER 将毫无用处:不可更改的遗留系统不知道它并且永远不会调用它。
        • 另外,过度设计。 IF ('SPROC_A_WRAPPER' = (SELECT FIRST 1 CALLING_PROC FROM GTT_CALLING_PROC ....) ) THEN ... 应该够了
        猜你喜欢
        • 2016-07-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-28
        • 1970-01-01
        • 2011-08-24
        • 1970-01-01
        相关资源
        最近更新 更多