Firebird SQL 过程可以知道调用它的父 procedure/trigger 吗?

Can Firebird SQL procedure know the parent procedure/trigger from which it is called from?

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

也许监控mon$...table数据可以给出答案?

适用于 Firebird 2.1 的问题

例如有 mon$call_stack table,但对于大多数 mon$... tables 对于 Firebird 2.1 来说是空的,它们会填充更高版本的 Firebird。

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

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

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

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;

但是,您必须在调用之前创建“一个特定过程”以正确设置此变量(这很乏味但并不难)并在调用后正确删除它(并且这应该被正确地构建以正确发生即使在 errors/exceptions 的情况下,这也是乏味且不容易的。

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

需要知道调用者的存储过程将在本例中被调用SPROC_A。

首先我们创建一个 Global Temp Table

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 table,调用它 SPROC_A,然后从 Temp Table 中删除该行,以防 SPROC_A从同一事务中的其他地方调用,它不会认为 SPROC_A_WRAPPER 调用了它。

虽然有些简陋,但相信能满足你的需要。