更新缓冲区并将锁定模式重置为原始模式

Update buffer and reset the locking-mode back to original one

假设我们有一个接受 buffer 参数的过程:

myprocedure.p:

DEF PARAM BUFFER bufferParameter FOR DatabaseTable.

/* Get the same buffer in EXCLUSIVE-LOCK for updating */
FIND CURRENT bufferParameter EXCLUSIVE-LOCK.

/* Update the value */
ASSIGN bufferParameter.MyField = "new value".

/* Reset the locking mode back to the original one? */
FIND CURRENT bufferParameter NO-LOCK.

这种方法的问题在于它可能会更改传递的 buffer parameter 的原始 locking-mode。缓冲区可能位于 EXCLUSIVE-LOCK 或 f.ex 中 NO-LOCK。通过更改 locking-mode,此过程可以调用使用相同 buffer.

的其他一些程序中的更新错误

另一种解决方案是为相同的 table 创建一个新的 temporary buffer,然后通过该缓冲区更新数据库(不触及传递的参数缓冲区)。这种方法的缺点是原来通过的buffer parameter会变成'out-of-date'。它需要一个新的数据库查询来更新它的 'MyField' 值以匹配数据库中的值。

如何在更新 fields 后将缓冲区的 locking-mode 重置回原来的 fields?

奇怪的是锁类型不是缓冲区句柄的属性。这似乎是一个明显的必要性。

您可以找到 recid 的 _lock 记录,但您还需要找到 table ID,这意味着在 handle:table.[=11= 的 _file 中查找它]

所以,假设你只连接了一个数据库,你会得到类似的东西:

FIND _file NO-LOCK WHERE _file-name = bufHandle:TABLE.
FIND _lock NO-LOCK WHERE _lock-name = USERID()
    AND _lock-table = _file-number
    AND _lock-recid = bufHandle:RECID.

如果记录存在,你就知道你有一把锁。 _lock_flags 会告诉你你有什么样的锁。我认为 S 是共享的,E 是独占的,并且认为如果有 Q 那么您正在等待那个锁,如果您已经在记录上锁定,我不会想到。

如果您的函数开始时记录未完全锁定,您可以将记录重新锁定在适当的状态。

当你在进行交易时,你实际上无法解锁这样的记录。如果您尝试,独占锁将变为边缘锁,直到事务结束并且更改被提交到数据库。这是为了防止数据完整性问题。如果您正在查看 _Lock-Flags 字段,则边缘锁将是 "L"。您还可以使用 Promon 检查锁定 table 状态。

根据您锁定和更新记录的方式,事务可能适用于不同的过程。您可以通过在代码中添加这一行来查看您是否正在进行交易:

MESSAGE TRANSACTION VIEW-AS ALERT-BOX.

这是关于记录锁定和事务范围界定的 OpenEdge 文档: https://documentation.progress.com/output/ua/OpenEdge_latest/index.html#page/gsabl/handling-data-and-locking-records.html#

这个线程可能是有用的背景 - 它讨论了如何找出当前锁定状态。


最终我认为这是您真正需要的:

    /* lightly tested psuedo code...
     */

    procedure updateMyField:

      define input parameter myRowid  as rowid.

      define buffer updMyTable for myTable.  /* this limits the scope of the buffer to this procedure */

      do for updMyTable transaction:  /* FOR "strong scopes" the buffer to this block */
        find updMyTable exclusive-lock where rowid( updMyTable ) = myRowid no-error.  /* the EXCLUSIVE-LOCK is scoped the FOR block -- when we leave the block it is released */
        if available updMyTable then
          assign
            updMyTable.myField = "new value"
          .
         else
          do:
            message "could not get an exclusive-lock on myTable".
            /* or whatever error handling you need... */
          end.
      end.  /* the transaction is committed and the lock is released -- no need to change its status */

      return.

    end.

    /* main block
     */

    find first myTable no-lock.
    display myTable.myField.
    pause.

    run updateMyField ( rowid( myTable )).

    find first myTable no-lock.
    display myTable.myField.
    pause.

我认为您可以使用第二种解决方案。定义一个本地缓冲区(无需将其称为临时缓冲区,这在 OpenEdge IMO 中没有明确定义的术语),使用 rowid 将参数的记录加载到此缓冲区中(如果参数缓冲区已被锁定,则基本上是一个自由操作)并更改您的本地缓冲区。 不要担心任何东西会过时。引用http://knowledgebase.progress.com/articles/Article/P4548(请阅读全文)

Only a single copy of a record is allowed to exist at any one time in the client record pool. This means that in the following case shows two buffers pointing at the same physical copy of customer 1. This means after executing the following code

DEFINE BUFFER cust FOR customer.
DEFINE BUFFER cust1 FOR customer.
FIND FIRST cust no-lock.
FIND cust1 where rowid(cust1) = rowid(cust) exclusive-lock.
cust1.name = 'xyz':U.

cust.name 也将具有值 'xyz'。 我只是尝试了代码来确定。确实我也确认了锁不在buffer上而是在record上。我也可以分配 cust.name 即使 cust 的查找使用无锁。 如果在您的 program/procedure 之外还没有事务,那么在离开过程时将释放锁。 绝对不要尝试使用 _lock ,除非有必要向用户显示谁正在锁定他想要更改的记录(或类似目的)。

所以当我认为当具有相同记录的另一个缓冲区更新了相同记录时原始缓冲区没有更新时,我发现我错了。

所以更新传递的缓冲区的一个好方法是通过 ROWID 获取相同的记录并分别在另一个缓冲区中更新它:

DEF BUFFER myFirstBuffer FOR MyTable.
DEF BUFFER mySecondBuffer FOR MyTable.

FIND FIRST myFirstBuffer WHERE myFirstBuffer.MyField = "myvalue" NO-LOCK.

/* At this moment myFirstBuffer.MyField is "myvalue" */

FIND FIRST mySecondBuffer WHERE ROWID(mySecondBuffer) = ROWID(myFirstBuffer) EXCLUSIVE-LOCK.
ASSIGN mySecondBuffer.MyField = "mynewvalue".

/* Now both 'myFirstBuffer.MyField' and 'mySecondBuffer.MyField' match ("mynewvalue")! */

Progress 似乎可以处理所有数据更改并相应地更新缓冲区。但是,我认为如果有 2 个单独的程序 运行、f.ex myprogram1.pmyprogram2.p 并且它们都在不同的时间更新相同的记录,那么这将不起作用(虽然这是另一个故事......)。 早些时候,当触发器影响的字段未在缓冲区上更新时,TRIGGERs 出现了一些问题。

但简而言之,我认为最好使用 ROWID 单独查询记录并以这种方式更新它。我确实相信开发人员不应该使用 FIND CURRENT myBuffer NO-LOCK.,因为这会更改记录的 locking-mode,并且可能会在以后使用 myBuffer 破坏逻辑。