Strong Scope Use

Krishan Gopal

New Member
Hello everyone!

I know there are Strong Scope in Progress with REPEAT FOR and DO FOR blocks.
But I nver saw any use of these in my career of 4.5 years.

Could please anyone help me to find where exactly we can make use of Strong scope in programming?

Thanks!
 

TomBascom

Curmudgeon
If you haven't seen it used in 4.5 years you must be working with some fairly awful code.

Any time that you update data would be a good place to make use of strong scope. For instance:

Code:
define buffer xcust for cust.

for each cust no-lock:

  if cust.discount > 10 then
    do FOR xcust transaction: /* strong scope the "xcust" buffer */
      find xcust exclusive-lock where xcust.custNum = cust.custNum.
      xcust.discount = 10.
    end.

end.

This is, obviously, a silly example. But perhaps you see the point.
 

Krishan Gopal

New Member
Thanks Tom for replying.

This is the good example, what I understand from this example is that we can reduce the scope of transaction specific to this block using DO FOR block. It is good practice when we have a lengthy code inside FOR EACH block and we are updating data too, at this place we can reduce the scope of transaction by using STRONG SCOPE. Am I right?

But I am afraid to share, I never saw this kind of coding, what normally I saw, take this example what you gave, here we will take this record again with exclusive-lock without using any block and buffer and will update it.
 

RealHeavyDude

Well-Known Member
There is a difference between buffer and transaction scope. In practice that could mean that a transaction has been committed but, as the buffer scope is weak, the client process may still hold a share-lock on the record until the buffer goes out of scope which might cause other processes hitting the lock wait timeout period ...

As Tom said, whenever you update the database ( or a TEMP-TABLE ) it is always good practice to use defined buffers ( especially for TEMP-TABLE ) and strong scoping to make the buffer scope equal to the transaction scope to avoid problems like I mentioned above in the first place.

You can use the LISTING option to the COMPILE statement to get an output where you can see the buffer and transaction scopes and they don't match per default.


Heavy Regards,
RealHeavyDude.
 

Krishan Gopal

New Member
Here is one example, I am confused with these examples as given in Programming hand book -

TEST1.p
********************************************************************


DEFINE BUTTON upd LABEL "Update".
DEFINE VAR current-cust AS RECID.

FORM
customer.cust-num name SKIP
upd
WITH FRAME main-frame.

ON CHOOSE OF upd DO:
DO TRANSACTION:
FIND customer WHERE RECID(customer) = current-cust EXCLUSIVE-LOCK.
UPDATE customer.name WITH FRAME main-frame.
RELEASE customer.
END.
END.

FIND FIRST customer NO-LOCK.

current-cust = RECID(customer).

DISP cust-num name WITH FRAME main-frame.
ENABLE upd WITH FRAME main-frame.

WAIT-FOR WINDOW-CLOSE OF CURRENT-WINDOW.
*******************************************************************

Here in this TEST1.p, if we do not use release statement in Transaction block, then record will be down graded to SHARE-LOCK as if record is already read before transaction start, then it leaves record in SHARE-LOCK, not in NO-LOCK. So another user can't update record till this window is closed as Record is scoped to whole procedure. It's fine clear as it is given in Book. But in below example it creates confusion to me -

TEST2.p
***************************************************************************

DEFINE BUTTON upd LABEL "Update".
DEFINE VAR current-cust AS RECID.

FORM
customer.cust-num name SKIP
upd
WITH FRAME main-frame.

ON CHOOSE OF upd DO:
RUN test3.p (current-cust).
END.

FIND FIRST customer NO-LOCK.

current-cust = RECID(customer).

DISP cust-num name WITH FRAME main-frame.
ENABLE upd WITH FRAME main-frame.

WAIT-FOR WINDOW-CLOSE OF CURRENT-WINDOW.
***********************************************************************

TEST3.p
*******************************************************************************
DEFINE INPUT PARAM curr-rec AS RECID.

DO TRANSACTION:
FIND customer WHERE RECID(customer) = curr-rec EXCLUSIVE-LOCK.
UPDATE customer.name WITH FRAME main-frame.
RELEASE customer.
END.
*****************************************************************

Here in TEST2.p and TEST3.p it doesn't behave as like TEST1.p. Here release statement releases only updated value to DB. and down grade lock to SHARE-LOCK.
In TEST2.p Record is also scoped to whole TEST2.p.

Please help me to clear this confusion.
 

TomBascom

Curmudgeon
Examples from the documentation should never be thought of as good programming.

They are intended to quickly demonstrate specific features -- they almost always contain numerous really bad programming practices. I don't know which particular example this is so I cannot comment on what features it is attempting to demonstrate but I can say that it is awful code that should never form the basis of a real program.

Also, any time you think you should be using RELEASE you are almost certainly doing something wrong.

Lastly, when you post code samples you should wrap them in [ C O D E ] tags. Like this:

Code:
display "please use  [ c o d e ]  tags!".

It makes them much, much easier to read.
 

RealHeavyDude

Well-Known Member
I totally agree with Tom. Most examples you see in Progress documentation are just showing a language feature but rarely are they best practice overall.

You should make yourself familiar with the concept of buffer and transaction scope. This is essential knowledge to be able to write code that won't cause bad things.

In a nutshell: Each database table comes with a default buffer associated with it with the same name that the table. When you reference the database table in reality you reference the default buffer associated with it. The outer most reference of these buffer defines the buffer scope which is always extended to the next outer block with scoping capabilities ( a simple DO block does not have scoping capabilities unless you specify FOR ... or TRANSACTION ). That's why this is a weak buffer scope.

In your examples the FIND statement defines the buffer scope which is then scoped to the next outer block which is the procedure itself. Since you use the default buffer too to update the database, at the end of the transaction scope the EXCLUSIVE-LOCK is downgraded to a SHARE-LOCK. This is Progress default behavior. Using the release statement is not really good practice because it may cause logic after the transaction to fail. To avoid the problem in the first place it is good practice to use a defined buffer and strong scoping when you update the database. This will also make your error handling much more transparent. If you don't you will exactly experience what you've seen and, once again, it's expected behavior.


Heavy Regards, RealHeavyDude.
 

Krishan Gopal

New Member
Thanks a lot to everyone.

Sorry, but I was not taking these above examples as best coding practice. In Programing hand book they gave the solution same what you (Tom and RealHeavyDude) suggested, scoping the buffer to DO FOR block rather than to whole procedure.

Here my doubt was, as Buffer is scoped to external procedure in both the programs TEST1.p and TEST2.p but in first TEST1.p RELEASE satement will release the buffer, while in TEST2.p it doesn't. It is confusing me. Please give me your important clarification.

Thanks & Regards, Krishan Gopal
 

RealHeavyDude

Well-Known Member
Don't be offended, but I don't see much benefit in elaborating on why bad practice behaves different than even worse practice and to be honest I am not 100% positive since I can't remember when I did not use strong scoping and defined buffers for updating the database.

My suspicion is that the reason for that could be that in the first example the buffer scope is not extended beyond the scope of the procedure, whereas in the second example it is. But that's speculation ...

Heavy Regards, RealHeavyDude.
 
Top