Buffer

Hello Everyone, hope you all are well. :)

I have few queries related to buffer:

1. As per my understanding each DB table has its default buffer with the same name, then why to create buffer with the same name as table name, apparently it works the same as default buffer. please suggest.

2. We can control the scope by using buffer but when we update any record of local buffer(defined in internal procedure) then corresponding original buffer gets updated. for ex:
Code:
RUN l-proc1.

PROCEDURE l-proc1:
DEF BUFFER buf-cust FOR customer.
  FOR EACH buf-cust WHERE balance > 25000 EXCLUSIVE-LOCK:
        UPDATE customer.ADDRESS. 
  END.
END PROCEDURE.

FOR EACH CUSTOMER WHERE balance > 25000 NO-LOCK:
      DISPLAY ADDRESS.
END.
/*HERE I WILL GET UPDATED ADDRESS FROM BUFFER buf-cust*/

As per this situation, I unable to figure out the advantage of limiting the scope of buffer to its internal procedure.

Please Suggest.

Thanks & Regards!
Rajat.
 

TheMadDBA

Active Member
1. Buffers in general are very useful to be able to look at several different records from the same table at the same time. It is simply an alias and not making a new copy of the table/records.

2. The purpose of limiting the scope is to keep the transaction scope as small as possible and avoid any easy ways of increasing the transaction scope. Without the buf-cust references to the customer table can increase the transaction scope and/or cause lock bleeding (having the record still locked after the transaction is completed).

For clarity and enforcement I prefer to use DO FOR buf-cust TRANSACTION to show exactly where I want the transaction to begin and end. If I am mistaken to the real transaction/buffer scope Progress will give me a nice compile error to set me straight... or the next guy to touch the code.
 
Hi DBA,

1. I am still stuck with my first point, Let me elaborate it .
For ex:
Code:
DEF BUFFER b-cust1 for customer.
FOR EACH b-cust1:
/**/
END.
FOR EACH customer:
/**/
END.
1. For first FOR EACH block : An alias(b-cust1) of customer table is crated and bring into memory?
2. For second FOR EACH block : Default Customer aliase of customer table is created and bring into memory?

And if i do something like that:
Code:
DEF BUFFER customer for customer.

If it works the same way that i mentioned above(2nd case) then why we create same buffer as table name. if no then how does it works?

2. For my second concern, I didn't understand the lock bleeding point that you mentioned above. It there anything that i can go through or something like that?

Please suggest.

Thanks & Regards!
Rajat.
 

TheMadDBA

Active Member
Lets start with the first level of confusion... There is one and only one copy of the customer table. No matter what you call it there is still one copy on disk and in memory.

A more realistic example would be needing to compare the values of one customer to another customer (or order,item,etc). Without buffers there is no way for you to have both records available to your code at the same time unless you copy the first record to a temp-table or variables or whatever.
 

GregTomkins

Active Member
In your example, the buffer is indeed useless. But here is a made-up example of where it would be useful, and this kind of thing comes up all the time:

  1. FOR EACH customer:
  2. /* This will get a different customer, without affecting the customer referenced by the FOR EACH */
  3. FIND b-cust1 WHERE b-cust1.name = customer.name_of_friend.
  4. DISPLAY customer.name "is friends with" b-cust1.name.
  5. END.
Also, we use explicitly named buffers a lot to protect an internal procedure from screwing with the buffers that might be used by a caller. For example, this pattern is, IMO, usually bad:

FIND customer "1".
RUN do_thing().
PROCEDURE do_thing:
FIND customer "2".
END.

The problem here is that the outer procedure should not have to worry about do_thing changing the currently active record. To avoid that, we would normally do this:

FIND customer "1".
RUN do_thing().
PROCEDURE do_thing:
DEF BUFFER bf_customer FOR customer.
FIND bf_customer "2".
END.

As others are bound to point out, there are newer and arguably better ways to do this now. But the key point is, this kind of isolation is super important (in a big code base anyway). People like to complain about global variables, and rightly so, but this is a more insiduous form of global variables that doesn't always get the derision it deserves.

EDIT: Remove code tags. Curse inability to find code tags button in PT. Curse apparent complexity of code tags generated by prior posters. Give up.
 
Hi Everyone,

I am still unable to understand the concept of taking buffer name same as table name. because if i do something like that:
Code:
DEF BUFFER customer for customer.
1. Then how AVM will get to know that, we are creating the new buffer or its the default one.?
2. If AVM treats both the same way, then where is the difference?
Perhaps my questions are too generic, but i am totally stuck with buffer concept. :(

Thanks & Regards!
Rajat.
 

GregTomkins

Active Member
Ha, it never occurred to me that you could do that. I would have expected a syntax error. I don't know how you could even tell which buffer is being used. Maybe it's actually the same one and the DEF BUFFER does nothing.

I think this probably falls into that general category of things you CAN do, but SHOULDN'T do.
--
The buffer concept is simple - like madDBA said, it's simply a way of looking at different records from the same table at the same time without having to fiddle around with making copies.
 

TheMadDBA

Active Member
Defining buffers with the same name as the table is awful and should probably be a bug. When I run into code like that it is usually a product of trying to fix record scoping issues (especially locking) without changing a lot of code.

Rajat - you should probably spend some timing reading the ABL Essentials documentation. Focus on the "Record Buffers and Record Scope" section to start. It actually does a decent job of explaining how buffers and record scope work and could/should be used.

Just don't copy the sample code too closely :)
 
Hi Greg, DBA,

Initially i thought the same, that crating buffer with same table name doesn't make sense. Because i saw this kind of stuff used in my local shop so i searched around and found some related stuff:-

This link specify the usage but i didn't understand this either.
Code:
http://stackoverflow.com/questions/5487889/creating-a-buffer-with-the-same-name-as-the-database-table

Please suggest.

Thanks & Regards!
Rajat.
 

TheMadDBA

Active Member
Like we said before... It is a hack and a bad practice. Read up on the documentation on the buffer/record scope and it should become clearer.
 
Yes definitely, i will go through that. I have Prohand book for progress, is there anything specific that you can suggest or should i go ahead with the same.

Thanks a lot for your mentorship.

Thanks & Regards!
Rajat.
 

Cringer

ProgressTalk.com Moderator
Staff member
We've got named buffers with the same name as the table all over our code. The main reason for it, as MadDBA says, is to avoid having to do massive code changes to fix scoping issues. It tends to be in functions where someone has realised it changes the current record and they don't want it to, so rather than having a named buffer and changing the code someone has just defined a self-named buffer. It's terrible to maintain.
 

Stefan

Well-Known Member
I always define temp-tables with 'silly' names that prevent me from using the default buffer and define (locally scoped) buffers with a buffer name.

Code:
define temp-table p_ttcountries no-undo
   field country as char.

procedure foo:
   
   define buffer ttcountry for p_ttcountries.

   for each ttcountry:
   end.

end procedure.

For database tables I am happy with locally scoped buffers that use the table name.

I used to have an issue with should a table name be plural or singular. A plural always seemed silly from a progress point of view and made sense from a sql point of view:

Code:
/* progress - seems silly */
for each customers:
end.

/* sql - makes sence */
select * from customers;

/* progress - makes sense */
for each customer:
end.

/* sql - seems silly */
select * from customer;

And then later on realized that my issue was a result of the default buffer that progress defines for you. Table names should be plural, buffers should be singular.
 

TomBascom

Curmudgeon
Au contraire "define buffer customer for customer." is, IMHO, excellent practice.

It is especially powerful and useful in internal procedures, functions and methods. It totally prevents accidental scope borrowing when someone forgets to use the gobbledygook prefixed buffer name imposed by that all time worst practice known as "hungarian notation".
 
Top