如何在 Informix 中检索活动保存点的名称

How to retrieve the name of the active SAVEPOINT in Informix

Informix(v12 或更高版本)是否有方法检索当前保存点的名称?

在 Oracle 中有类似的东西:您可以使用 SET TRANSACTION NAME 命名事务,然后 select 来自 v$transaction:

的事务名称
SELECT name 
    FROM v$transaction
    WHERE xidusn
          || '.'
          || xidslot
          || '.'
          || xidsqn = DBMS_TRANSACTION.LOCAL_TRANSACTION_ID;

这不是很简单,但可以解决问题。实际上,我们可以使用它来拥有一个交易范围的变量(是的,这很丑陋,但它已经工作多年了)。

我们有一个基于此的机制,并希望将其移植到 Informix。有没有办法做到这一点?

当然,如果有一种不同的机制提供事务范围的变量(所以 DEFINE GLOBAL 不是我们正在寻找的),那也会有帮助,但我怀疑是否存在。

谢谢大家到目前为止的评论。

让我展示一下我想出的解决方案。这只是一个进行中的想法,但我希望它能引领某个方向: 我需要一个 "audit_lock" table,它始终包含当前交易的记录,携带有关当前交易的信息,尤其是用户名和唯一的 transaction_id(UUID 或类似的)。该行将在开始事务时插入并在提交之前删除。

然后我有一个包含审核信息的通用 audit_trail table。

所有审计的 table 都使用触发器填充通用审计跟踪 table,将每个审计列序列化为通用审计跟踪的单独记录 table。

audit_lock和audit_trailtable需要使用行锁定。另外,为了避免 audit_lock table 上的读锁,我们需要将隔离级别设置为 COMMITTED READ LAST COMMITTED。 如果您的用例不支持,则建议的模式不起作用。

这是 DDL:

CREATE TABLE audit_lock
(
   transaction_id varchar(40) primary key,
   username varchar(40)
);

alter table audit_lock
lock mode(ROW);

CREATE TABLE audit_trail
(
   id       serial primary key,
   tablename varchar(255) NOT NULL,
   record_id numeric(10) NOT NULL,
   username varchar(40) NOT NULL,
   transaction_id varchar(40) NOT NULL,
   changed_column_name varchar(40),
   old_value varchar(40),
   new_value varchar(40),
   operation varchar(40) NOT NULL,
   operation_date datetime year to second NOT NULL
);

alter table audit_trail
lock mode(ROW);

现在我们需要审核 table:

CREATE TABLE audited_table
(
   id serial,
   somecolumn varchar(40)
);

并且 table 有一个插入触发器写入 audit_trail:

CREATE PROCEDURE proc_trigger_audit_audited_table () 
REFERENCING OLD AS o NEW AS n FOR audited_table;

INSERT INTO audit_trail
(
  tablename,
  record_id,
  username,
  transaction_id,
  changed_column_name,
  old_value,
  new_value,
  operation,
  operation_date
)
VALUES
(
  'audited_table',
  n.id,
  (SELECT username FROM audit_lock),
  (SELECT transaction_id FROM audit_lock),
  'somecolumn',
  '',
  n.somecolumn,
  'INSERT',
  sysdate
);

END PROCEDURE;

CREATE TRIGGER audit_insert_audited_table INSERT ON audited_table REFERENCING NEW AS post
  FOR EACH ROW(EXECUTE PROCEDURE proc_trigger_audit_audited_table() WITH TRIGGER REFERENCES);

现在让我们使用它:首先交易的调用者需要为自己生成一个transaction_id,可能使用UUID生成机制。在下面的示例中,transaction_id 只是“4711”。

BEGIN WORK;

SET ISOLATION TO COMMITTED READ LAST COMMITTED; --should be set globally 

-- Issue the generation of the audit_lock entry at the beginnig of each transaction 
insert into audit_lock (transaction_id, username) values ('4711', 'userA');

-- Is it there?
select * from audit_lock;

-- do data manipulation stuff
insert into audited_table (somecolumn) values ('valueA');

-- Issue that at the end of each transaction 
delete from audit_lock
where transaction_id = '4711';

commit;

在快速测试中,所有这些甚至在同时进行的交易中也能正常工作。当然,这还需要大量的工作和测试,但我目前希望这条路是可行的。

让我再补充一点关于我们在 Oracle 中使用的其他方法的更多信息: 在 Oracle 中,我们(滥用)使用事务名​​称来准确存储上述建议中存储在 audit_lock table.

中的信息

其余同上。触发器在那个特定的应用程序中完美地工作,即使当然有很多其他应用程序的场景,在每个 table 上放置插入、删除和更新触发器,为 [=64= 中的每个更改的列生成记录] 会疯的。在我们的应用程序中,它完美运行了十年,并且对应用程序的使用方式没有明显的性能影响。

在 java 应用程序服务器中,所有正在更改数据的代码块,首先从设置事务名称开始,然后对各种 table 进行大量更改,这可能会发布所有这些触发器。所有这些都 运行 在同一个事务中,并且由于它有一个包含应用程序用户的事务名称,触发器可以将该信息写入审计跟踪 table。

我知道还有其他方法可以解决这个问题,你甚至可以只使用休眠功能来做到这一点,但我们的方法允许我们通过数据库强制执行一些一致性(审计跟踪中的 NOT NULL 约束 table 在用户名上)。由于一切都是通过触发器完成的,如果事务名称不包含用户(通过要求它采用特定格式),我们可以让它们失败。如果应用程序的任何其他部分、其他应用程序或无知的管理员试图在不考虑将事务名称设置为特定格式的情况下向已审核的 table 发布更新,这些更新将失败。这使得更新已审核的 tables,不生成所需的审核 table 条目更难(当然不是不可能,当然,一个不情愿的管理员可以做任何事情)。

现在所有畏缩的人,让我引用 Luis 的话:这似乎是个糟糕的主意,但我有我的用例;)

@Luís 在每个事务中创建一个特定的 table 来存储信息的想法导致 systables 中的锁定问题。我们称之为 "transaction info table"。我没有想到这个想法,因为 DDL 会导致 Oracle 中的提交。所以我在 Informix 中尝试过,但是如果我尝试在两个同时的事务中创建一个名为 "tblX" 的 table,则第二个事务会出现锁定异常:

Cannot update system catalog (systables). [SQL State=IX000, DB Errorcode=-312] 
Next: ISAM error: key value locked [SQL State=IX000, DB Errorcode=-144]

但据我现在测试,让所有交易都使用与上述相同的 table 是可行的。