AutoEdge - Scope of the dataset in a da-instance

Posted by Admin on 29-Jan-2007 13:01

One of the oldest things in the 4GL is buffer scoping. Nowadays we have some syntax to decouple the buffer from the definition (see the BIND-discussion http://www.psdn.com/library/thread.jspa?threadID=2891&tstart=0).

When I look at the daCar.p procedure in AutoEdge I see something that makes me shiver:

- definition of datasets

- passing of handles to override the globally defined dataset

That's one of the things I really dislike about todays ABL, since it's very error prone.

When I look at beCar.p calling daCar.p, I see the handle magic demonstrated:

PROCEDURE fetchWhere :

DEFINE OUTPUT PARAMETER DATASET-HANDLE phDataSet .

phDataSet = DATASET dsCar:HANDLE.

RUN fetchWhere IN hAccessProc

(OUTPUT DATASET dsCar BY-REFERENCE).

END PROCEDURE. /* fetchWhere */

We pass our beCar.p globally defined dataset to our daCar.p and let it populate that one. So what happens when you call this procedure twice during a task?

All Replies

Posted by Admin on 29-Jan-2007 13:56

Another thing I don't like in the daCar and sceCar routines is the dynamic buffer handling, bypassing compile time checks on the buffers. Things like:

ASSIGN hCurBuffer = phDataSet:get-buffer-handle("eCar":U).

cCurCarId = hCurBuffer:buffer-field("CarId"):buffer-value.

in sceCar.p.

I know that, when you start passing a dataset handle, you're forced to make another procedure call to cast the handle back to a static definition.

I also know that you're forced to use a handle when you don't want to populate the dataset defined at the global procedure level.

Maybe the sce-procedures could be used with static defined buffer parameters, so at least the sce-routines can do a simple FIND/FOR EACH to populate the datasets...

Posted by Admin on 29-Jan-2007 15:37

When I look at the daCar.p procedure in AutoEdge I

see something that makes me shiver:

- definition of datasets

- passing of handles to override the globally defined

dataset

That's one of the things I really dislike about

todays ABL, since it's very error prone.

The dataset definition in daCar.p should be made REFERENCE-ONLY. This way, it's less error prone because you can't use the globally define dataset, only the one passed using either the BY-REFERENCE or BIND option.

When I look at beCar.p calling daCar.p, I see the

handle magic demonstrated:

PROCEDURE fetchWhere :

DEFINE OUTPUT PARAMETER DATASET-HANDLE phDataSet

.

phDataSet = DATASET dsCar:HANDLE.

RUN fetchWhere IN hAccessProc

(OUTPUT DATASET dsCar BY-REFERENCE).

PROCEDURE. /* fetchWhere */

We pass our beCar.p globally defined dataset to our

daCar.p and let it populate that one. So what happens

when you call this procedure twice during a task?

Looking at the fetchWhere in daSupport.p, the dataset is emptied prior to the fill.

Posted by Admin on 30-Jan-2007 03:17

The dataset definition in daCar.p should be made

REFERENCE-ONLY. This way, it's less error prone

because you can't use the globally define dataset,

only the one passed using either the BY-REFERENCE or

BIND option.

Yes.

If you want to go in this direction maybe it would be better to wrap the dataset by a class and exchange a class instance, simulating a resultset object. This way the dataset will always have a parent: the class instance wrapping it.

Posted by Admin on 30-Jan-2007 03:25

According to KB-entry P70795 the OUTPUT dataset handle should be deleted at the end of the procedure, so

PROCEDURE fetchWhere :

DEFINE OUTPUT PARAMETER DATASET-HANDLE phDataSet.

phDataSet = DATASET dsCar:HANDLE.

RUN fetchWhere IN hAccessProc (OUTPUT DATASET dsCar BY-REFERENCE).

PROCEDURE. /* fetchWhere */

Should be:

PROCEDURE fetchWhere :

DEFINE OUTPUT PARAMETER DATASET-HANDLE phDataSet.

phDataSet = DATASET dsCar:HANDLE.

RUN fetchWhere IN hAccessProc (OUTPUT DATASET dsCar BY-REFERENCE).

DELETE OBJECT phDataSet.

PROCEDURE. /* fetchWhere */

Posted by Admin on 30-Jan-2007 09:28

Another thing I don't like in the daCar and sceCar

routines is the dynamic buffer handling, bypassing

compile time checks on the buffers. Things like:

ASSIGN hCurBuffer =

phDataSet:get-buffer-handle("eCar":U).

cCurCarId =

hCurBuffer:buffer-field("CarId"):buffer-value.

in sceCar.p.

I know that, when you start passing a dataset handle,

you're forced to make another procedure call to cast

the handle back to a static definition.

I also know that you're forced to use a handle when

you don't want to populate the dataset defined at the

global procedure level.

Maybe the sce-procedures could be used with static

defined buffer parameters, so at least the

sce-routines can do a simple FIND/FOR EACH to

populate the datasets...

From what I understand, when separating DSO from DAO object, the DSO become unaware of the dataset structure. One reason is for being able to re-use the DSO with different dataset. This means the DSO routines have to receive the dataset by DATASET-HANDLE.

However, the DSO should still know the temp-table which he's supposed to fill.

As you said, maybe the dataset's temp-table handle could be cast to a static tt, then this tt could be use statically inside DSO routines.

Here's a little example that seems to work but hasn't been thoroughly tested.

/ sceXXX.p /

/* Include the tt definition as reference-only.

It will be bound to the tt in the dataset-handle received. */

DEF TEMP-TABLE tt REFERENCE-ONLY

FIELD f1 AS CHAR

FIELD f2 AS INTE.

PROCEDURE AfterRowFill:

DEF INPUT PARAM DATASET-HANDLE phDataSet.

DEF VAR htt AS HANDLE.

/* Dynamically get the temp-table handle from the dataset.

Then bind it to the statically defined one using DynamicToStatic procedure.*/

ASSIGN htt = phDataSet:GET-BUFFER-HANDLE("tt"):TABLE-HANDLE.

RUN DynamicToStaticTT(INPUT TABLE-HANDLE htt BIND).

/* This message show that the static tt is now bound the dynamic one,

as it displays the same handle. */

MESSAGE htt TEMP-TABLE tt:HANDLE

VIEW-AS ALERT-BOX INFO BUTTONS OK.

/* Can read and create records statically */

FOR EACH tt:

DISPLAY tt.

END.

CREATE tt.

ASSIGN tt.f1 = "def"

tt.f2 = 456.

END PROCEDURE.

PROCEDURE DynamicToStaticTT:

DEF INPUT PARAM TABLE FOR tt BIND.

MESSAGE TEMP-TABLE tt:HANDLE

VIEW-AS ALERT-BOX INFO BUTTONS OK.

END PROCEDURE.

Posted by Thomas Mercer-Hursh on 01-Feb-2007 13:59

I seem to recall

this suggestion somewhere ... Among other things, it keeps one

from having to have temp-table definitions all over the place.

Posted by Admin on 01-Feb-2007 14:39

I seem to recall this suggestion somewhere ...

Yes, I even thought about quoting you, but I was a bit worried for your reply This approach simply wraps the data container like an internal Data Transfer Object, so it wouldn't be a full blown domain object... And here we are again: use the ABL strength (and weaknesses) using buffers/temp-tables or using domain objects and don't use/expose buffers/temp-tables at all....

Posted by Thomas Mercer-Hursh on 01-Feb-2007 14:47

To be sure, I would tend to go all the way, but I think any amount of wrapping illustrates the point.

This thread is closed