There are several ways you may define your own data types in ECL. The RECORD and TYPE structures are the most common.
The RECORD structure can be likened to a struct in the C/C++ languages. It defines a related set of fields that are the fields of a recordset, whether that recordset is a dataset on disk, or a temporary TABLE, or the result of any operation using a TRANSFORM function.
The RECORD structure is a user-defined data type because, once defined as an attribute you may use that attribute as:
* the data type for parameters passed to TRANSFORM functions
* the data type for a "field" in another RECORD structure (nested structures)
* the structure of a nested child DATASET field in another RECORD structure
Here's an example that shows all three uses (contained in the RecStruct.ECL file) :
IMPORT ProgrammersGuide.DeclareData AS ProgGuide; Layout_Person := RECORD UNSIGNED1 PersonID; STRING15 FirstName; STRING25 LastName; END; Person := DATASET([{1,'Fred','Smith'}, {2,'Joe','Blow'}, {3,'Jane','Smith'}],Layout_Person); Layout_Accounts := RECORD STRING10 Account; UNSIGNED4 Balance; END; Layout_Accounts_Link := RECORD UNSIGNED1 PersonID; Layout_Accounts; //nested RECORD structure END; Accounts := DATASET([{1,'45621234',452}, {1,'55621234',5000}, {2,'45629876',4215}, {3,'45628734',8525}],Layout_Accounts_Link); Layout_Combined := RECORD Layout_Person; DATASET(Layout_Accounts) Accounts; //nested child DATASET END; P_recs := PROJECT(Person, TRANSFORM(Layout_Combined,SELF := LEFT; SELF := [])); Layout_Combined CombineRecs(Layout_Combined L, Layout_Accounts_Link R) := TRANSFORM SELF.Accounts := L.Accounts + ROW({R.Account,R.Balance}, Layout_Accounts); SELF := L; END; //input and output types NestedPeopleAccts := DENORMALIZE(P_recs, Accounts, LEFT.personid=RIGHT.personid, CombineRecs(LEFT,RIGHT)); OUTPUT(NestedPeopleAccts);
The Layout_Accounts_Link contains Layout_Accounts. There is no field name given to it, which means that it simply inherits all the fields in that structure, as they are defined, and those inherited fields are referenced as if they were explicitly declared in the Layout_Accounts_Link RECORD structure, like this:
x := Accounts.Balance;
However, if a name had been given to it, then it would define a nested structure and the fields in that nested structure would have to be referenced using the nested structure's name as part of the qualifier, like this:
//Assuming the definition was this: Layout_Accounts_Link := RECORD UNSIGNED1 PersonID; Layout_Accounts AcctStruct; //nested RECORD with name END; //then the field reference would have to be this: x := Accounts.AcctStruct.Balance;
The Layout_Accounts RECORD structure attribute defines the structure of the child DATASET field in Layout_Combined. The Layout_Combined RECORD structure is then used as the LEFT input and output for the CombineRecs TRANSFORM function.
The TYPE structure is an obvious user-defined type because you are defining a data type that is not already supported in the ECL language. Its purpose is to allow you to import data in whatever format you receive it, work with it in one of the internal formats, then re-write the data in its original format back to disk.
It works by defining specific callback functions inside the TYPE structure (LOAD, STORE, etc.) that the system will use to read and write the data from and to disk. The LOAD callback function reads the data from disk and defines the internal type the data will be as you work with it as the return data type from the LOAD function you write.
GetXLen(DATA x,UNSIGNED len) := TRANSFER(((DATA4)(x[1..len])),UNSIGNED4); xstring(UNSIGNED len) := TYPE EXPORT INTEGER PHYSICALLENGTH(DATA x) := GetXLen(x,len) + len; EXPORT STRING LOAD(DATA x) := (STRING)x[(len+1)..GetXLen(x,len) + len]; EXPORT DATA STORE(STRING x):= TRANSFER(LENGTH(x),DATA4)[1..len] + (DATA)x; END; pstr := xstring(1); // typedef for user defined type pppstr := xstring(3); nameStr := STRING20; // typedef of a system type namesRecord := RECORD pstr surname; nameStr forename; pppStr addr; END; ds := DATASET([{'TAYLOR','RICHARD','123 MAIN'}, {'HALLIDAY','GAVIN','456 HIGH ST'}], {nameStr sur,nameStr fore, nameStr addr}); namesRecord MoveData(ds L) := TRANSFORM SELF.surname := L.sur; SELF.forename := L.fore; SELF.addr := L.addr; END; out := PROJECT(ds,MoveData(LEFT)); OUTPUT(out);
This example defines a "Pascal string" data type with the leading length stored as one to four bytes prepended to the data.
The TypeDef attribute is another obvious user-defined type because you are defining a specific instance of a data type that is already supported in the ECL language as a new name, either for convenience of maintenance or code readability purposes. The above example also demonstrates the use of TypeDef attributes.