buffers and assign statement

rithmikansur

New Member
Im a bit confused about buffers.
I'm currently running 9.1D

I had defined a shared buffer.
then sent the handle to that buffer as input to an internal procedure
the procedure was then supposed to update one of the fields in the buffer.
However, it didn't work ask expected. It didn't generate an error, but it also didn't update the record.

So, i ended using the below code do do my updating, where i check the buffer name then update the appropriate buffer.
The ReqNum field is the primary key BTW.

Code:
PROCEDURE myUpdateHold:
  DEF INPUT PARAMETER thisBuffer AS HANDLE.
  DEF INPUT PARAMETER ReqNum AS INTEGER NO-UNDO.
  CASE STRING(thisBuffer:Name):
                                  WHEN "DesSched" THEN DO:
                                       FIND DesSched WHERE DesSched.ReqNum = ReqNum  EXCLUSIVE-LOCK.
                                       IF DesSched.Hold = TRUE
                                          THEN ASSIGN DesSched.Hold = FALSE.
                                          ELSE ASSIGN DesSched.Hold = TRUE.
                                  END.
                                  WHEN "DesComp" THEN DO:
                                       FIND DesComp WHERE DesComp.ReqNum = ReqNum  EXCLUSIVE-LOCK.
                                       IF DesComp.Hold = TRUE
                                          THEN ASSIGN DesComp.Hold = FALSE.
                                          ELSE ASSIGN DesComp.Hold = TRUE.
                                  END.
                                  WHEN "EstSched" THEN DO:
                                       FIND EstSched WHERE EstSched.ReqNum = ReqNum  EXCLUSIVE-LOCK.
                                       IF EstSched.Hold = TRUE
                                          THEN ASSIGN EstSched.Hold = FALSE.
                                          ELSE ASSIGN EstSched.Hold = TRUE.
                                  END.
                                  WHEN "EstComp" THEN DO:
                                       FIND EstComp WHERE EstComp.ReqNum = ReqNum  EXCLUSIVE-LOCK.
                                       IF EstComp.Hold = TRUE
                                          THEN ASSIGN EstComp.Hold = FALSE.
                                          ELSE ASSIGN EstComp.Hold = TRUE.
                                  END.
                              END CASE.
  APPLY 'CHOOSE' TO but-refresh.
END PROCEDURE.


Should i be using the recid or rowid ? or am i going about this the wrong way?
Any help is appreciated.
Thanks!
John
 

rzr

Member
if possible stay away from using anything that is SHARED.
you did'nt post the code that is not working .. did you?
 

rzr

Member
here's a ex from the handbook - let us know if this does'nt work for you:

Code:
[FONT=courier new]DEFINE BUTTON btnFind LABEL "Find Customer".[/FONT][FONT=courier new]
 
DO WITH FRAME frCustomer WITH SIDE-LABELS:[/FONT][FONT=courier new]
 
  ENABLE Customer.Cust-Num btnFind.[/FONT][FONT=courier new]
 
  ON CHOOSE OF btnFind DO: [/FONT][FONT=courier new]
 
    RUN getCustomer (Customer.Cust-Num:HANDLE, BUFFER Customer). [/FONT][FONT=courier new]
    IF NOT ERROR-STATUS:ERROR THEN[/FONT][FONT=courier new]
      DISPLAY Customer EXCEPT Comments WITH SIDE-LABELS.[/FONT][FONT=courier new]
  END.[/FONT][FONT=courier new]
 
  ON ENTRY OF Customer.Cust-Num[/FONT][FONT=courier new]
    HIDE MESSAGE.[/FONT][FONT=courier new]
END.[/FONT][FONT=courier new]
 
WAIT-FOR WINDOW-CLOSE OF CURRENT-WINDOW.  [/FONT][FONT=courier new]
 
PROCEDURE getCustomer:[/FONT][FONT=courier new]
 
  DEFINE INPUT PARAMETER  hWidget     AS HANDLE.[/FONT][FONT=courier new]
  DEFINE PARAMETER BUFFER bufCustomer FOR Customer.[/FONT][FONT=courier new]
 
  FIND bufCustomer WHERE bufCustomer.Cust-Num = [/FONT][FONT=courier new]
    INTEGER(hWidget:SCREEN-VALUE) NO-LOCK NO-ERROR.[/FONT][FONT=courier new]
 
  IF NOT AVAILABLE bufCustomer THEN DO:[/FONT][FONT=courier new]
    MESSAGE "Customer record not found." VIEW-AS ALERT-BOX.[/FONT][FONT=courier new]
    RETURN ERROR.[/FONT][FONT=courier new]
  END. [/FONT][FONT=courier new]
END.[/FONT][FONT=courier new]
[/FONT]
 

rithmikansur

New Member
After reading your comment about avoiding shared buffers, and looking more closely at the example.
I think i know what i did wrong. I was passing the handle of the buffer and not defining a locally scoped buffer for the internal procedure.
So, i tried to make a shared buffer. Anyway, I'll give it a go in the morning and let you know how i made out.
Thank you.
John
 

RealHeavyDude

Well-Known Member
There is more to that: You should only mix and match static and dynamic references for a buffer unless you are sure what you are doing.

A static buffer is one that you DEFINE and is resolved at compile time. You can always grab the handle to that buffer using something like
ASSIGN hBufferHandle = BUFFER myBuffer:HANDLE.
From that moment on you can work with that buffer as you would with any other buffer that you've created dynamically. From that perspective they are identical.

But, when it comes to scope, they behave completely different. The scope of a static buffer is resolved at compile time and is automatically extended to the next outer block with scoping capabilities. The scope of a dynamic buffer is resolved at runtime. It will always end up in a widget pool which is either scoped to the session or a procedure and is either unnamed or named. The most infamous one is the unnamed one scoped to the session which is the default one - meaning that, if you don't specify otherwise explicitly, handle-based dynamic objects will end up there.

Back to your issue: Whenever you change a buffer you should do so using a named buffer and strong scoping. Otherwise, if you are not 100% positive about what you are doing, you will end with behavior as you described.

DEFINE BUFFER b_Customer FOR Customer.

DO FOR b_Customer TRANSACTION:
FIND b_Customer EXCLUSIVE-LOCK WHERE ...
/* Your code to update the buffer here */
END.

Note that a DO block does not have scoping capabilities unless you specify them explicitly using strong scoping and the TRANSACTION keyword ( for transaction scope respectively ).


Heavy Regards, RealHeavyDude.
 

SergioC

Member
Hi John, perhaps this example will serve.

Code:
Code:
/* Connect to Sport2000 */
DEFINE BUTTON btnFind LABEL "Find Number".


DEFINE VARIABLE c-buffer AS CHAR INITIAL 'Customer,Item'.


DEFINE VARIABLE rs-buffer AS CHAR VIEW-AS RADIO-SET 
    RADIO-BUTTONS 'Customer','Customer,CustNum,Name',
                  'Item','Item,ItemNum,ItemName'
    VERTICAL.


DEFINE VARIABLE i-number AS INTEGER NO-UNDO.




DO WITH FRAME frNumber WITH SIDE-LABELS:
 
  ENABLE rs-buffer i-number btnFind.
 
  ON 'VALUE-CHANGED':U OF rs-buffer IN FRAME frNumber
  DO:
      ASSIGN rs-buffer.
  END.


  ON CHOOSE OF btnFind DO: 
    RUN setData (i-Number:HANDLE). 
  END.


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


PROCEDURE setData:
 
  DEFINE INPUT PARAMETER hNumber    AS HANDLE.
  DEFINE VARIABLE hBufData          AS HANDLE.
  DEFINE VARIABLE hQuery            AS HANDLE.
  DEFINE VARIABLE hField            AS HANDLE.
 
  CREATE BUFFER hBufData FOR TABLE ENTRY(1,rs-buffer).
  CREATE QUERY hQuery.
  hQuery:SET-BUFFERS(hBufData).
  hQuery:QUERY-PREPARE(SUBSTITUTE("FOR EACH &1 WHERE &2 = &3 " , 
                                   ENTRY(1,rs-buffer), 
                                   ENTRY(2,rs-buffer), 
                                   hNumber:SCREEN-VALUE)).
  hQuery:QUERY-OPEN.


  DO TRANSACTION:
      hQuery:GET-FIRST(EXCLUSIVE-LOCK).
      hField = hBufData:BUFFER-FIELD(ENTRY(3,rs-buffer)).


      MESSAGE hField:NAME + ':'  
              hField:BUFFER-VALUE VIEW-AS ALERT-BOX.
      /*
      hField:BUFFER-VALUE = hField:BUFFER-VALUE + ' XXX'.
      */
      hField:BUFFER-VALUE = REPLACE(hField:BUFFER-VALUE,' XXX','').
      
      MESSAGE hField:NAME + ':'  
              hField:BUFFER-VALUE VIEW-AS ALERT-BOX.


      hQuery:QUERY-CLOSE.
  END.


  DELETE WIDGET hBufData.
  DELETE WIDGET hQuery.


END.

Regards.
 

rithmikansur

New Member
The code i wrote is ugly. But, it is functioning. So, I was taken off it for now.
However, probably over the upcoming weekend, I'll get back on it because it just feels wrong the way it is now.

I feel like i should be able to pass the buffer by name (or handle) to a procedure as input, and use something like.

Code:
DEFINE BUFFER mybuffer FOR Customer.
DEFINE BUFFER myotherbuffer FOR Vendor.

RUN updatecity (INPUT BUFFER mybuffer).
RUN updatecity (INPUT BUFFER myotherbuffer).

PROCEDURE updatecity:
   DEF INPUT PARAMETER BUFFER thisbuffer.  
   ASSIGN thisbuffer.city = "somevalue".
END PROCEDURE.

It seems like it should be possible. So, clearly i'm missing something.
Again i appreciate the help. the feedback has been great.
I'll hit the documentation some more over the weekend and let you know how it goes.
John
 

Marian EDU

Member
I feel like i should be able to pass the buffer by name (or handle) to a procedure as input, and use something like.

but you should be able to do it, just that not passing a buffer parameter which is static but a buffer handle... well, it's simply a handle (I wish they'll add buffer-handle same way we have table-handle, dataset-handle) so you better check if the handle is really a buffer handle beside the obvious check to see if the handle is valid :)

if the handle is a valid buffer handle and it's available you can attempt to set values in it's fields, again on the wish list would be to have an option to check lock-level... if the assign fails then you might try to bufferHdl:find-current(exclusive-lock, no-wait) and try again if you were able to lock that record for update :)

for assign, if you know the field name then you can use the shortcut bufferHdl::<fieldName>, if not you have to go through bufferHdl:buffer-field("fieldName"):buffer-value.

Code:
PROCEDURE updatecity: 
   DEF INPUT PARAMETER thisbuffer AS HANDLE. 
  
   IF NOT VALID-HANDLE(thisbuffer) OR thisbuffer:TYPE NE 'BUFFER' 
      THEN      RETURN ERROR.
   
   IF thisbuffer:AVAILABLE THEN 
      ASSIGN thisbuffer::city = "somevalue". 
      /* OR */ 
      ASSIGN thisbuffer:buffer-field("city"):buffer-value = "somevalue". 
  END PROCEDURE.
 

rithmikansur

New Member
Marian,
your code sample worked like a charm.
it appears the piece i was missing was the FIND-CURRENT() method.
All i needed to do was add that, and wrap it in a DO TRANSACTION block.
Also, nice tip on the shortcut for naming the field buffhndl::fieldname.
That also worked like a charm.
Very cool.
Thank you all so much!
 

KleineCuypie

New Member
Hi,

Looks like there is already a good answer. That's great.
Anyway I'd like to share something with u guys.

It's also possible to pass a buffer like this:

Code:
FIND FIRST customer WHERE nr = 1 NO-LOCK.
IF AVAIL customer THEN RUN updateCustomer(BUFFER customer) /* or INPUT BUFFER customer */


PROCEDURE updateCustomer:
   DEFINE PARAMETER BUFFER b_customer FOR customer.
   b_customer::value = value.
   /*b_customer can be used as a normal buffer now.*/
END PROCEDURE.

This worked fine for me ;).
Hope this could/can help you too.

Greetz KleineCuypie.
 
Top