Dataset with before-fill on buffer

Posted by Ron Nijssen on 25-Aug-2015 02:22

Hello,

I have a dataset where i want to fill the top-buffer myself. If all tables are coming from the database, all works as expected, but if i don't supply a data-source for ttOrder but instead create the same ttOrder record myself, i don't get the same results.

See the example below. If lCustomFill is false, you will get results, if it is true and thus creating the order record yourself, you won't get the same result.

What am i missing?

DEFINE VARIABLE lCustomFill AS LOGICAL INITIAL false.
DEFINE TEMP-TABLE ttOrder NO-UNDO LIKE Order.
DEFINE TEMP-TABLE ttOline NO-UNDO LIKE OrderLine.
DEFINE TEMP-TABLE ttItem  NO-UNDO LIKE Item.

DEFINE DATASET dsOrder FOR ttOrder, ttOLine, ttItem
DATA-RELATION drOrderLine FOR ttOrder, ttOLine RELATION-FIELDS (OrderNum,OrderNum)
DATA-RELATION drLineItem  FOR ttOLine, ttItem  RELATION-FIELDS (ItemNum,ItemNum)
.

IF lCustomFill THEN
  DATASET dsOrder::ttOrder:SET-CALLBACK-PROCEDURE ("BEFORE-FILL", "ipBeforeFillOrder").
ELSE DO:
  DEFINE QUERY qOrder FOR Order.
  DEFINE DATA-SOURCE srcOrder FOR QUERY qorder Order KEYS (Ordernum).
  BUFFER ttOrder:ATTACH-DATA-SOURCE(DATA-SOURCE srcOrder:HANDLE,?,?).
  QUERY qOrder:QUERY-PREPARE("FOR EACH ORDER WHERE OrderNum EQ 7").
END.

DEFINE DATA-SOURCE srcOLine FOR OrderLine KEYS (Ordernum).
DEFINE DATA-SOURCE srcItem  FOR Item KEYS (Itemnum).

BUFFER ttOLine:ATTACH-DATA-SOURCE(DATA-SOURCE srcOLine:HANDLE,?,?).
BUFFER ttItem :ATTACH-DATA-SOURCE(DATA-SOURCE srcItem :HANDLE,?,?).

DATASET dsOrder:FILL().

FOR EACH ttOrder:
  DISP ttOrder.
END.

FOR EACH ttItem:
  DISP ttItem.
END.

BUFFER ttOrder:DETACH-DATA-SOURCE().
BUFFER ttOLine:DETACH-DATA-SOURCE().
BUFFER ttItem :DETACH-DATA-SOURCE().

PROCEDURE ipBeforeFillOrder:
  DEFINE INPUT PARAMETER DATASET FOR dsOrder.
  FIND Order 7 NO-LOCK.
  CREATE ttOrder.
  BUFFER-COPY Order TO ttOrder.
END PROCEDURE.

Posted by Chad Thomson on 25-Aug-2015 08:10

The magic is really a combination of the existence of a Data-Source and the Data-Relations.  


The DataSet begins a FILL operation with the top-level buffer(s) and cascades the FILL to all the child buffers described by the Data-Relations.  FYI: a 'top-level' buffer or 'top-buffer' is one that is NOT a child of any Data-Relation.


With a Data-Source attached to a member buffer, the DataSet issues a FILL and monitors the Data-Source for indication it should continue the FILL.


Without a Data-Source attached, the internal DataSet FILL mechanism expects application logic to exist that will perform the remainder of the work.  Creating a TT record alone does not indicate to the Dataset that the child FILL operation should occur.  


What needs to happen: fill all 'children' of each top-level buffer.  That relationship is described by the Data-Relations. 

Only those Data-Relations which contain ttOrder as a parent-buffer and are active need to be filled. 


Examples to perform this might be, replace your internal "ipBeforeFillOrder" procedure with the following:

<snip>

PROCEDURE ipBeforeFillOrder:
  DEFINE INPUT PARAMETER DATASET FOR dsOrder.

  FIND Order 7 NO-LOCK.
  CREATE ttOrder.
  BUFFER-COPY Order TO ttOrder.
   
   /* Remember: it is this ttOrder record (buffer) that is in scope that will be used for all child FILL operations */

    /* 1. we know 'ttoline' is the only *immediate* child of ttOrder - fill it */
    /* buffer ttOline:fill(). */

    /* 2. we know there is only one top-buffer (ttorder) and one data-relation of interest (drOrderLine) */
    /*
    dataset dsOrder
       :get-relation(1) /* get-relation ('drOrderLine')
       :child-buffer
       :fill()
       .
    */

    /* 3. the above examples do not consider:
              could be more than one top-buffer
              top-buffer could be on several data-relations
              data-relations may not be ACTIVE
    */
    /* ttOrder is A 'top-buffer' for the dataset */
    /* trigger the FILL for any 'immediate' children of this buffer */
    /* children described by: dataset:data-relation(N):child-buffer */
    def    var      iloop as integer        no-undo.
    def    var      irels  as integer        no-undo.
    def    var      hrel  as handle no-undo.
    def    var      htopbuffer as handle no-undo.
            
    assign             /* only interested in ttOrder at this time */
       htopbuffer = dataset dsorder:get-top-buffer (1)  /* buffer ttorder:handle */
       irels          = dataset dsorder:num-relations
       .
    
    /* look at DS relations... */   
    do iloop = 1 to irels:
        assign
           hrel = dataset dsorder:get-relation (iloop)
           .

        /* that are active and have 'this buffer' as parent */
        if (hrel:active) and (hrel:parent-buffer eq htopbuffer) then
        do:
            /* FYI: could inspect "fill-mode" of the child buffer */

            /* issue FILL on the child buffer */
            hrel:child-buffer:fill().
        end.        
    end.

    assign
          hrel           = ?
          htopbuffer = ?
          .
END PROCEDURE.

</snip>


Hope that helps somewhat.


Chad R. Thomson
Senior Principal Solutions Consultant
5000 Peachtree Ind. Blvd. | Suite 100 | Norcross, GA 30071
Office: 770.449.9696 x233027


[collapse]
From: Ron Nijssen <bounce-RonN@community.progress.com>
Sent: Tuesday, August 25, 2015 03:23
To: TU.OE.Development@community.progress.com
Subject: [Technical Users - OE Development] Dataset with before-fill on buffer
 
Thread created by Ron Nijssen

Hello,

I have a dataset where i want to fill the top-buffer myself. If all tables are coming from the database, all works as expected, but if i don't supply a data-source for ttOrder but instead create the same ttOrder record myself, i don't get the same results.

See the example below. If lCustomFill is false, you will get results, if it is true and thus creating the order record yourself, you won't get the same result.

What am i missing?

DEFINE VARIABLE lCustomFill AS LOGICAL INITIAL false.
DEFINE TEMP-TABLE ttOrder NO-UNDO LIKE Order.
DEFINE TEMP-TABLE ttOline NO-UNDO LIKE OrderLine.
DEFINE TEMP-TABLE ttItem  NO-UNDO LIKE Item.

DEFINE DATASET dsOrder FOR ttOrder, ttOLine, ttItem
DATA-RELATION drOrderLine FOR ttOrder, ttOLine RELATION-FIELDS (OrderNum,OrderNum)
DATA-RELATION drLineItem  FOR ttOLine, ttItem  RELATION-FIELDS (ItemNum,ItemNum)
.

IF lCustomFill THEN
  DATASET dsOrder::ttOrder:SET-CALLBACK-PROCEDURE ("BEFORE-FILL", "ipBeforeFillOrder").
ELSE DO:
  DEFINE QUERY qOrder FOR Order.
  DEFINE DATA-SOURCE srcOrder FOR QUERY qorder Order KEYS (Ordernum).
  BUFFER ttOrder:ATTACH-DATA-SOURCE(DATA-SOURCE srcOrder:HANDLE,?,?).
  QUERY qOrder:QUERY-PREPARE("FOR EACH ORDER WHERE OrderNum EQ 7").
END.

DEFINE DATA-SOURCE srcOLine FOR OrderLine KEYS (Ordernum).
DEFINE DATA-SOURCE srcItem  FOR Item KEYS (Itemnum).

BUFFER ttOLine:ATTACH-DATA-SOURCE(DATA-SOURCE srcOLine:HANDLE,?,?).
BUFFER ttItem :ATTACH-DATA-SOURCE(DATA-SOURCE srcItem :HANDLE,?,?).

DATASET dsOrder:FILL().

FOR EACH ttOrder:
  DISP ttOrder.
END.

FOR EACH ttItem:
  DISP ttItem.
END.

BUFFER ttOrder:DETACH-DATA-SOURCE().
BUFFER ttOLine:DETACH-DATA-SOURCE().
BUFFER ttItem :DETACH-DATA-SOURCE().

PROCEDURE ipBeforeFillOrder:
  DEFINE INPUT PARAMETER DATASET FOR dsOrder.
  FIND Order 7 NO-LOCK.
  CREATE ttOrder.
  BUFFER-COPY Order TO ttOrder.
END PROCEDURE.

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by Mike Fechner on 25-Aug-2015 08:13

Depending on the use case, it might be easier to use a temp-table as the source-table for the first ProDataset table.

That source temp-table could be filled in the BEFORE-FILL callback for the dataset.

You will end up with the twice the data for that temp-table: Once in the source table and once in the ProDataset table.

But it might help keeping code more standard.

All Replies

Posted by Chad Thomson on 25-Aug-2015 08:10

The magic is really a combination of the existence of a Data-Source and the Data-Relations.  


The DataSet begins a FILL operation with the top-level buffer(s) and cascades the FILL to all the child buffers described by the Data-Relations.  FYI: a 'top-level' buffer or 'top-buffer' is one that is NOT a child of any Data-Relation.


With a Data-Source attached to a member buffer, the DataSet issues a FILL and monitors the Data-Source for indication it should continue the FILL.


Without a Data-Source attached, the internal DataSet FILL mechanism expects application logic to exist that will perform the remainder of the work.  Creating a TT record alone does not indicate to the Dataset that the child FILL operation should occur.  


What needs to happen: fill all 'children' of each top-level buffer.  That relationship is described by the Data-Relations. 

Only those Data-Relations which contain ttOrder as a parent-buffer and are active need to be filled. 


Examples to perform this might be, replace your internal "ipBeforeFillOrder" procedure with the following:

<snip>

PROCEDURE ipBeforeFillOrder:
  DEFINE INPUT PARAMETER DATASET FOR dsOrder.

  FIND Order 7 NO-LOCK.
  CREATE ttOrder.
  BUFFER-COPY Order TO ttOrder.
   
   /* Remember: it is this ttOrder record (buffer) that is in scope that will be used for all child FILL operations */

    /* 1. we know 'ttoline' is the only *immediate* child of ttOrder - fill it */
    /* buffer ttOline:fill(). */

    /* 2. we know there is only one top-buffer (ttorder) and one data-relation of interest (drOrderLine) */
    /*
    dataset dsOrder
       :get-relation(1) /* get-relation ('drOrderLine')
       :child-buffer
       :fill()
       .
    */

    /* 3. the above examples do not consider:
              could be more than one top-buffer
              top-buffer could be on several data-relations
              data-relations may not be ACTIVE
    */
    /* ttOrder is A 'top-buffer' for the dataset */
    /* trigger the FILL for any 'immediate' children of this buffer */
    /* children described by: dataset:data-relation(N):child-buffer */
    def    var      iloop as integer        no-undo.
    def    var      irels  as integer        no-undo.
    def    var      hrel  as handle no-undo.
    def    var      htopbuffer as handle no-undo.
            
    assign             /* only interested in ttOrder at this time */
       htopbuffer = dataset dsorder:get-top-buffer (1)  /* buffer ttorder:handle */
       irels          = dataset dsorder:num-relations
       .
    
    /* look at DS relations... */   
    do iloop = 1 to irels:
        assign
           hrel = dataset dsorder:get-relation (iloop)
           .

        /* that are active and have 'this buffer' as parent */
        if (hrel:active) and (hrel:parent-buffer eq htopbuffer) then
        do:
            /* FYI: could inspect "fill-mode" of the child buffer */

            /* issue FILL on the child buffer */
            hrel:child-buffer:fill().
        end.        
    end.

    assign
          hrel           = ?
          htopbuffer = ?
          .
END PROCEDURE.

</snip>


Hope that helps somewhat.


Chad R. Thomson
Senior Principal Solutions Consultant
5000 Peachtree Ind. Blvd. | Suite 100 | Norcross, GA 30071
Office: 770.449.9696 x233027


[collapse]
From: Ron Nijssen <bounce-RonN@community.progress.com>
Sent: Tuesday, August 25, 2015 03:23
To: TU.OE.Development@community.progress.com
Subject: [Technical Users - OE Development] Dataset with before-fill on buffer
 
Thread created by Ron Nijssen

Hello,

I have a dataset where i want to fill the top-buffer myself. If all tables are coming from the database, all works as expected, but if i don't supply a data-source for ttOrder but instead create the same ttOrder record myself, i don't get the same results.

See the example below. If lCustomFill is false, you will get results, if it is true and thus creating the order record yourself, you won't get the same result.

What am i missing?

DEFINE VARIABLE lCustomFill AS LOGICAL INITIAL false.
DEFINE TEMP-TABLE ttOrder NO-UNDO LIKE Order.
DEFINE TEMP-TABLE ttOline NO-UNDO LIKE OrderLine.
DEFINE TEMP-TABLE ttItem  NO-UNDO LIKE Item.

DEFINE DATASET dsOrder FOR ttOrder, ttOLine, ttItem
DATA-RELATION drOrderLine FOR ttOrder, ttOLine RELATION-FIELDS (OrderNum,OrderNum)
DATA-RELATION drLineItem  FOR ttOLine, ttItem  RELATION-FIELDS (ItemNum,ItemNum)
.

IF lCustomFill THEN
  DATASET dsOrder::ttOrder:SET-CALLBACK-PROCEDURE ("BEFORE-FILL", "ipBeforeFillOrder").
ELSE DO:
  DEFINE QUERY qOrder FOR Order.
  DEFINE DATA-SOURCE srcOrder FOR QUERY qorder Order KEYS (Ordernum).
  BUFFER ttOrder:ATTACH-DATA-SOURCE(DATA-SOURCE srcOrder:HANDLE,?,?).
  QUERY qOrder:QUERY-PREPARE("FOR EACH ORDER WHERE OrderNum EQ 7").
END.

DEFINE DATA-SOURCE srcOLine FOR OrderLine KEYS (Ordernum).
DEFINE DATA-SOURCE srcItem  FOR Item KEYS (Itemnum).

BUFFER ttOLine:ATTACH-DATA-SOURCE(DATA-SOURCE srcOLine:HANDLE,?,?).
BUFFER ttItem :ATTACH-DATA-SOURCE(DATA-SOURCE srcItem :HANDLE,?,?).

DATASET dsOrder:FILL().

FOR EACH ttOrder:
  DISP ttOrder.
END.

FOR EACH ttItem:
  DISP ttItem.
END.

BUFFER ttOrder:DETACH-DATA-SOURCE().
BUFFER ttOLine:DETACH-DATA-SOURCE().
BUFFER ttItem :DETACH-DATA-SOURCE().

PROCEDURE ipBeforeFillOrder:
  DEFINE INPUT PARAMETER DATASET FOR dsOrder.
  FIND Order 7 NO-LOCK.
  CREATE ttOrder.
  BUFFER-COPY Order TO ttOrder.
END PROCEDURE.

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by Mike Fechner on 25-Aug-2015 08:13

Depending on the use case, it might be easier to use a temp-table as the source-table for the first ProDataset table.

That source temp-table could be filled in the BEFORE-FILL callback for the dataset.

You will end up with the twice the data for that temp-table: Once in the source table and once in the ProDataset table.

But it might help keeping code more standard.

Posted by Ron Nijssen on 26-Aug-2015 00:34

Thank you for your reply, that clarifies a lot. I didn't realize i should do the fill on the ttOrder myself, but i makes sense.

This thread is closed