Chapter 60. Migrating CTL1 to CTL2

When you want to migrate any transformation code written in CTL1 to CTL2, you need to make the following steps:

Step 1: Replace the header

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

CTL1CTL2
Interpreted mode
//#TL//#CTL2
//#CTL1
Compiled mode
unavailable//#CTL2:COMPILED

Step 2: Change declarations of primitive variables (integer, byte, decimal data types)

Both versions of CTL have the same variables, however, key words differ for some of them.

CTL1CTL2
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.

Step 3: Change declarations of structured variables: (list, map, record data types)

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

Step 4: Change declarations of functions

  1. Add return data types to declarations of functions. (Remember that there is also void return type in CTL2.)

  2. Add data types of their arguments.

  3. 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:

CTL1CTL2
function transform(idx) {
     <other function body>
     $0.customer := $0.customer;
}
function integer transform(integer idx) {
     <other function body>
     $0.customer = $0.customer;
     return 0;
}

Step 5: Change record syntax

In CTL1, @ sign was used in assignments of input records and fields.

In CTL2, other syntax should be used. See the following table:

CTL1CTL2
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

Note that you should also change the syntax of groupAccumulator usage in Rollup.

CTL1CTL2
groupAccumulator["<field name>"]groupAccumulator.<field name>

Step 6: Replace dictionary functions with dictionary syntax

In CTL1, a set of dictionary functions may be used.

In CTL2, dictionary syntax is defined and should be used.

CTL1CTL2
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

CTL1CTL2
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");

Step 7: Add semicolons where necessary

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.

Step 8: Check, replace, or rename some built-in CTL functions

Some CTL1 functions are not available in CTL2. Please check Functions Reference for list of CTL2 functions.

Example:

CTL1 functionCTL2 function
uppercase()upperCase()
bit_invert()bitNegate()

Step 9: Change switch statements

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

Step 10: Change sequence and lookup table syntax

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 versionSequence 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 versionLookup 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]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 lookup_found(<lookup table ID>) function for CTL1 is not too recommended either.

Step 11: Change mappings in functions

Rewrite the mappings according to CTL2 syntax. Change mapping operators and remove expressions that use @ as shown above.

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