When you want to migrate any transformation code written in CTL1 to CTL2, you need to make the following steps:
Replace the header which is at the beginning of your transformation code.
CTL1 used either //#TL
, or //#CTL1
whereas CTL2 uses //#CTL2
for interpeted mode.
Remember that you can also choose compiled mode which is not available in CTL1. In such a case, header in CTL2 would be: //#CTL2:COMPILED
CTL1 | CTL2 |
---|---|
Interpreted mode | |
//#TL | //#CTL2 |
//#CTL1 | |
Compiled mode | |
unavailable | //#CTL2:COMPILED |
Both versions of CTL have the same variables, however, key words differ for some of them.
The integer
data type is declared using the int
word in CTL1, whereas it is declared as integer
in CT2.
The byte
data type is declared using the bytearray
word in CTL1, whereas it is declared as byte
in CT2.
The decimal
data type may contain Length
and Scale
in its declaration in CTL1.
For example, decimal(15,6) myDecimal;
is valid declaration of a decimal in CTL1 with Length
equal to 15 and Scale
equal to 6.
In CTL2, neither Length
nor Scale
may be used in a declaration of decimal
.
By default any decimal variable may use up to 32 digits plus decimal dot in its value.
Only when such decimal is sent to an edge, in which Length
and Scale
are defined in metadata (by default they are 8 and 2), precission or length may change.
Thus, equivalent declaration (in CTL2) would look like this:
decimal myDecimal;
Decimal field defines these Length and Scale in metadata. Or uses the default 8 and 2, respectively.
CTL1 | CTL2 |
---|---|
Integer data type | |
int myInt; | integer myInt; |
Byte data type | |
bytearray myByte; | byte myByte; |
Decimal data type | |
decimal(15,6) myDecimal; | decimal myDecimal; |
If such variable should be assigned to a decimal field,
the field should have defined Length and Scale to 15 and 6, respectively. |
Each list
is a uniform structure consisting of elements of the same data type.
The list
is declared as folows in CTL1 (for example):
list myListOfStrings;
Equivalent list
declaration would look like the following in CTL2:
string[] myListOfStrings;
Declaration of any list
in CTL2 uses the following syntax:
<data type of element>[] identifier
Thus, replace the declaration of a list
of CTL1 with another, valid in CTL2.
Each map
is a uniform structure consisting of key-value pairs. Key is always of string
data type, whereas value
is of any primitive data type (in CTL1).
Map declaration may look like this:
map myMap;
Unlike in CTL1, in addition to string
, key may be of any other primitive data type in CTL2.
Thus, in CTL2 you need to specify both key type and value type like this:
map[<key type>, <value type>] myMap;
In order to rewrite your map declarations fom CTL1 syntax to that of CTL2, replace the older declaation of CTL1:
map myMap;
with the new of CTL2:
map[string, <value type>] myMap;
For example, map[string, integer] myMap;
Each record
is a heterogeneous structure consisting of specified number of fields. Each field can be of different primitive data type. Each field has its name and its number.
In CTL1, each record may be declared in three different ways:
Two of them use metadata ID, the third uses port number.
Unlike in CTL1, where metadata are idetified with their ID, in CTL2 metadata are identified by their unique name.
See the table below on how records may be declared in CTL1 and in CTL2.
CTL1 | CTL2 |
---|---|
Declaration of a list | |
list myList; | <element type>[] myList; |
e.g.: string[] myList; | |
Declaration of a map | |
map myMap; | map[<type of key>,<type of value>] myMap; |
e.g.: map[string,integer] myMap; | |
Declaration of a record | |
record (<metadata ID>) myRecord; | <metadata name> myRecord; |
record (@<port number>) myRecord; | |
record (@<metadata name>) myRecord; |
Add return data types to declarations of functions. (Remember that there is also void
return type in CTL2.)
Add data types of their arguments.
Each function that returns any data type other than void
must end with a return
statement.
Add corresponding return
statement when necessary.
See following table:
CTL1 | CTL2 |
---|---|
function transform(idx) { <other function body> $0.customer := $0.customer; } | function integer transform(integer idx) { <other function body> $0.customer = $0.customer; return 0; } |
In CTL1, @ sign was used in assignments of input records and fields.
In CTL2, other syntax should be used. See the following table:
CTL1 | CTL2 |
---|---|
Whole record | |
<record variable name> = @<port number>; |
<record variable name>.* = $<port number>.*; |
<record variable name> = @<metadata name> |
<record variable name>.* = $<metadata name>.*; |
Individual field | |
@<port number>[<field number>] |
$<port number>.<corresponding field name> |
@<metadata name>[<field number>] |
$<metadata name>.<corresponding field name> |
<record variable name>["<field name>"] |
<record variable name>.<field name> |
Note | |
---|---|
Note that you should also change the syntax of |
CTL1 | CTL2 |
---|---|
groupAccumulator["<field name>"] | groupAccumulator.<field name> |
In CTL1, a set of dictionary functions may be used.
In CTL2, dictionary syntax is defined and should be used.
CTL1 | CTL2 |
---|---|
Writing to dictionary | |
write_dict(string <entry name>, string <entry value>); |
dictionary.<entry name> = <entry value>; |
dict_put_str(string <entry name>, string <entry value>); | |
Reading from dictionary | |
string myVariable; myVariable = read_dict(<entry name>); |
string myVariable; myVariable = dictionary.<entry name>; |
string myVariable; myVariable = dict_get_str(<entry name>); |
Example 60.1. Example of dictionary usage
CTL1 | CTL2 |
---|---|
Writing to dictionary | |
write_dict("Mount_Everest", "highest"); |
dictionary.Mount_Everest = "highest"; |
dict_put_str("Mount_Everest", "highest"); | |
Reading from dictionary | |
string myVariable; myVariable = read_dict("Mount_Everest"); |
string myVariable; myVariable = dictionary.Mount_Everest; |
string myVariable; myVariable = dict_get_str("Mount_Everest"); |
In CT1, jump
, continue
, break
,
or return
statement sometime do not allow terminal semicolon.
In CTL2, it is even required.
Thus, add semicolons to the end of any jump
, continue
, break
,
or return
statement when necessary.
Some CTL1 functions are not available in CTL2. Please check Functions Reference for list of CTL2 functions.
Example:
CTL1 function | CTL2 function |
---|---|
uppercase() | upperCase() |
bit_invert() | bitNegate() |
Replace expressions in the case
parts of switch
statement with constants.
Note that CTL1 allows usage of expressions in the case
parts of the switch
statements,
it requires curle braces after each case
part. Values of one or more expression may even equal to each other, in such a case, all statements are executed.
CTL2 requires usage of constants in the case
parts of the switch
statements, it does not allow curle braces after each case
part,
and requires a break
statement at the end of each case
part. Without such break
statement, all statements below would be executed.
The constant specified in different case
parts must be different.
CTL version | Switch statement syntax |
---|---|
CTL1 | //#CTL1 int myInt; int myCase; myCase = 1; // Transforms input record into output record. function transform() { myInt = random_int(0,1); switch(myInt) { case (myCase-1) : { print_err("two"); print_err("first case1"); } case (myCase) : { print_err("three"); print_err("second case1"); } } $0.field1 := $0.field1; return 0 } |
CTL2 | //#CTL2 integer myInt; // Transforms input record into output record. function integer transform() { myInt = randomInteger(0,1); switch(myInt) { case 0 : printErr("zero"); printErr("first case"); break; case 1 : printErr("one"); printErr("second case"); } $0.field1 = $0.field1; return 0; } |
In CTL1, metadata, lookup tables, and sequences were identified with their IDs.
In CTL2 they are identified with their names.
Thus, make sure that all metadata, lookup tables, and sequences have unique names. Otherwise, rename them.
The two tables below show how you shout change the code containing lookup table or sequence syntax. Note that these are identified with either IDs (in CTL1) or with their names.
CTL version | Sequence syntax |
---|---|
CTL1 | //#CTL1 // Transforms input record into output record. function transform() { $0.field1 := $0.field1; $0.field2 := $0.field2; $0.field3 := sequence(Sequence0).current; $0.field4 := sequence(Sequence0).next; $0.field5 := sequence(Sequence0, string).current; $0.field6 := sequence(Sequence0, string).next; $0.field7 := sequence(Sequence0, long).current; $0.field8 := sequence(Sequence0, long).next; return 0 } |
CTL2 | //#CTL2 // Transforms input record into output record. function integer transform() { $0.field1 = $0.field1; $0.field2 = $0.field2; $0.field3 = sequence(seqCTL2).current(); $0.field4 = sequence(seqCTL2).next(); $0.field5 = sequence(seqCTL2, string).current(); $0.field6 = sequence(seqCTL2, string).next(); $0.field7 = sequence(seqCTL2, long).current(); $0.field8 = sequence(seqCTL2, long).next(); return 0; } |
CTL version | Lookup table usage |
---|---|
CTL1 | //#CTL1 // variable for storing number of duplicates int count; int startCount; // values of fields of the first records string Field1FirstRecord; string Field2FirstRecord; // values of fields of next records string Field1NextRecord; string Field2NextRecord; // values of fields of the last records string Field1Value; string Field2Value; // record with the same metadata as those of lookup table record (Metadata0) myRecord; // Transforms input record into output record. function transform() { // getting the first record whose key value equals to $0.Field2 // must be specified the value of both Field1 and Field2 Field1FirstRecord = lookup(LookupTable0,$0.Field2).Field1; Field2FirstRecord = lookup(LookupTable0,$0.Field2).Field2; // if lookup table contains duplicate records with the value specified above // their number is returned by the following expression // and assigned to the count variable count = lookup_found(LookupTable0); // it is copied to another variable startCount = count; // loop for searching the last record in lookup table while ((count - 1) > 0) { // searching the next record with the key specified above Field1NextRecord = lookup_next(LookupTable0).Field1; Field2NextRecord = lookup_next(LookupTable0).Field2; // decrementing counter count--; } // if record had duplicates, otherwise the first record Field1Value = nvl(Field1NextRecord,Field1FirstRecord); Field2Value = nvl(Field2NextRecord,Field2FirstRecord); // mapping to the output // last record from lookup table $0.Field1 := Field1Value; $0.Field2 := Field2Value; // corresponding record from the edge $0.Field3 := $0.Field1; $0.Field4 := $0.Field2; // count of duplicate records $0.Field5 := startCount; return 0 } |
CTL2 | //#CTL2 // record with the same metadata as those of lookup table recordName1 myRecord; // variable for storing number of duplicates integer count; integer startCount; // Transforms input record into output record. function integer transform() { // if lookup table contains duplicate records, // their number is returned by the following expression // and assigned to the count variable count = lookup(simpleLookup0).count($0.Field2); // This is copied to startCount startCount = count; // getting the first record whose key value equals to $0.Field2 myRecord = lookup(simpleLookup0).get($0.Field2); // loop for searching the last record in lookup table while ((count-1) > 0) { // searching the next record with the key specified above myRecord = lookup(simpleLookup0).next(); // decrementing counter count--; } // mapping to the output // last record from lookup table $0.Field1 = myRecord.Field1; $0.Field2 = myRecord.Field2; // corresponding record from the edge $0.Field3 = $0.Field1; $0.Field4 = $0.Field2; // count of duplicate records $0.Field5 = startCount; return 0; } |
Warning | |
---|---|
We suggest you better use other syntax for lookup tables. The reason is that the following expression of CTL2: lookup(Lookup0).count($0.Field2); searches the records through the whole lookup table which may contain a great number of records. The syntax shown above may be replaced with the following loop: myRecord = lookup(<name of lookup table>).get(<key value>); while(myRecord != null) { process(myRecord); myRecord = lookup(<name of lookup table>).next(); } Especially DB lookup tables can return -1 instead of real count of records with specified key value (if you do not set Max cached size to a non-zero value). The |
Rewrite the mappings according to CTL2 syntax. Change mapping operators and remove expressions that use @
as shown above.
CTL version | Transformation with mapping |
---|---|
CTL1 | //#TL int retInt; function transform() { if ($0.field3 < 5) retInt = 0; else retInt = 1; // the following part is the mapping: $0.* := $0.*; $0.field1 := uppercase($0.field1); $1.* := $0.*; $1.field1 := uppercase($0.field1); return retInt } |
CTL2 | //#CTL2 integer retInt; function integer transform() { // the following part is the mapping: $0.* = $0.*; $0.field1 = upperCase($0.field1); $1.* = $0.*; $1.field1 = upperCase($0.field1); if ($0.field3 < 5) return = 0; else return 1; } |