Reputation: 2096
Lets say we have a procedure which takes in a buffer
-parameter:
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.
The problem with this approach is that it is possibly changing the original locking-mode
of the passed buffer parameter
. It is possible that the buffer was in EXCLUSIVE-LOCK
or f.ex in NO-LOCK
. By changing the locking-mode
, this procedure could invoke updating errors in some other programs using this same buffer
.
Another solution would be to create a new temporary buffer
for the same table, and then update the database thru that buffer (without touching the passed parameter buffer). The con of this approach is that the original passed buffer parameter
will become 'out-of-date'. It would require a new database query to update it's 'MyField'
value to match the one in the database.
How I could reset the buffer's locking-mode
back to the original one after updating it's fields
?
Upvotes: 1
Views: 1727
Reputation: 2096
So I figured out that I was wrong when I thought that the original buffer was not updated when another buffer with that same record has updated the same record.
So a good way to update a passed buffer is to get the same record by ROWID
and update it in another buffer separately:
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")! */
The Progress
seems to handle all of the data changes and updates the buffers accordingly. However, I believe that this doesn't work if there are 2 separate programs running, f.ex myprogram1.p
and myprogram2.p
and they both update the same record at a different time (though this is another story...).
Earlier we had some problems with TRIGGER
s when the trigger affected fields were not updated on the buffer.
But in a nutshell, I think it's best to query the record separately with the ROWID
and update it that way. I do believe that the developer shouldn't use FIND CURRENT myBuffer NO-LOCK.
as that will change the locking-mode
of the record and could break logics using myBuffer
later.
Upvotes: 0
Reputation: 404
I think your second solution would be the one to use. Define a local buffer (no need to call it a temporary buffer which is not a clearly defined term in OpenEdge IMO), load the record of the parameter into this buffer using rowid (basically a free operation, if the parameter buffer was locked already) and change your local buffer. Don't worry about anything getting out of date. Quoting http://knowledgebase.progress.com/articles/Article/P4548 (please read the complete article)
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 would also have the value 'xyz'. I just tried the code to be sure. Indeed I also confirmed that the lock is not on the buffer but on the record. I could also assign cust.name even though the find to cust uses no-lock. If there is not already a transaction outside your program/procedure then the lock will be released when leaving the procedure. Definitely don't try to work with _lock unless it's necessary to show the user who is locking the record he wants to change (or similar purposes).
Upvotes: 0
Reputation: 14020
This thread might be useful background - it talks about how to figure out the current lock status.
http://stackoverflow.com/questions/26136842/progress-4gl-display-buffer-lock-type?rq=1
Ultimately I think this is what you really need:
/* 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.
Upvotes: 2
Reputation: 1217
You can't actually unlock a record like that when you're in a transaction. If you try, the exclusive lock becomes a limbo lock until the transaction ends and the changes are committed to the database. This is to prevent data integrity problems. If you're looking at the _Lock-Flags field, the limbo lock will be an "L". You can also use Promon to check the lock table status.
The transaction may be scoped to different procedures, depending on how you lock and update records. You can see if you're in a transaction by sticking this line in your code:
MESSAGE TRANSACTION VIEW-AS ALERT-BOX.
Here's the OpenEdge documentation on record locking and transaction scoping: https://documentation.progress.com/output/ua/OpenEdge_latest/index.html#page/gsabl/handling-data-and-locking-records.html#
Upvotes: 1
Reputation: 1367
It's bizarre that lock type is not an attribute of the buffer handle. It would seem to be an obvious necessity.
You can find the _lock records for the recid, but you need to find the table ID as well, which would mean looking it up in _file from the handle:table.
So, assuming you've only one database connected, you'd have something like:
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.
If the record exists, you know you have a lock. The _lock_flags will tell you what kind of locks you have. I think S is share and E is exclusive and think if there is a Q then you are waiting for that lock, which I wouldn't expect if you already have a lock on the record.
You could then re-lock the record in the appropriate state if it wasn't already exclusively locked when your function began.
Upvotes: 1