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;
} |