What’s in Your ODBC Driver? A 2024 Update Alan Seiden

​[[{“value”:”Since IBM announced Yum support for installing the IBM i Db2 ODBC driver, as documented in our 2022 tutorial, users can update their driver more easily. To help users determine what’s changed, IBM maintains a list of fixes and enhancements for each IBM i ODBC driver release. IBM i ODBC driver update pages IBM i, Mac, Linux, and Windows are given their own IBM i ODBC driver update pages: IBM i (PASE): https://www.ibm.com/support/pages/ibm-i-access-acs-updates-pase Mac: https://www.ibm.com/support/pages/ibm-i-access-acs-updates-mac Linux: https://www.ibm.com/support/pages/ibm-i-access-acs-updates-linux Windows: https://www.ibm.com/support/pages/ibm-i-access-acs-updates-windows Two kinds of updates The update pages (all except Windows) contain two main update sections: APAR Fixes: fixes based on customer…
The post What’s in Your ODBC Driver? A 2024 Update appeared first on Seiden Group.”}]] Read More 

Advent of Code 2024 day 2, RPG edition

​[[{“value”:”This is the second day of Advent of Code, let’s solve today’s puzzles with RPG.
The puzzle as well as the input data are available here.
The code is available here.
Part 1
We are provided with a (stream) file containing unusual data. Each line is a report. Each report contains multiple levels, separated by spaces.
Each report can be safe or unsafe. To be safe, the following conditions must be met:

the levels are either all increasing or all decreasing
any two adjacent levels differ by at least one and at most three

We need to calculate how many reports are safe.
The IFS file can easily be read using QSYS2.IFS_READ_UTF8 table function.
Each report can be devided into levels with the %SPLIT RPG built-in function. We can iterate over the levels extracted by %SPLIT with the for-each instruction.
On the first level, we don’t need to do any check.
On the second level, we determine if the report is increasing or decreasing and we check if it differs from the first one by a value of 1, 2 or 3.
Starting at the third level, we check if the direction of the report is correct and if the difference from the previous level is between 1 and 3.
As soon as a level is incorrect, the report is considered unsafe.
If we go over all levels without any incorrect check, the report is safe.
Here is the RPG code for part 1:
**free
ctl-opt dftactgrp(*no); // We’re using procedure so we can’t be in the default activation group

dcl-pi *n;
input char(50);
end-pi;

dcl-pr isSafe ind;
data varchar(255);
end-pr;

dcl-s fileName varchar(50);

dcl-s data varchar(255);
dcl-s safeReports int(10) inz(0);

fileName = %trim(input);

// Read the input data from the IFS one line at a time
exec sql declare c1 cursor for select cast(line as varchar(255)) from table(qsys2.ifs_read_utf8(path_name => :fileName));

exec sql open c1;

// Read first line
exec sql fetch from c1 into :data;

// Loop until the end of the file
dow sqlcode = 0;

// If the report is safe we increment the safe counter
if isSafe(data);
safeReports += 1;
endif;

// Read next line
exec sql fetch from c1 into :data;
enddo;

exec sql close c1;

snd-msg *info ‘Safe reports: ‘ + %char(safeReports) %target(*pgmbdy:1); // Send message with answer

*inlr = *on;
return;

dcl-proc isSafe;

dcl-pi *n ind;
data varchar(255);
end-pi;

dcl-s value char(5); // Current value as a string
dcl-s previous int(10); // Previous value
dcl-s current int(10); // Current value as an integer
dcl-s index int(10) inz(1); // Current index of the value in the report (starts at 1)
dcl-s increasing ind; // *on if the reports values are increasing, *off if decreasing

// Split the report into values and loop over each value
for-each value in %split(data);
current = %int(value); // Convert the current value to integer

if index > 1; // We only start testing at the second value
if index = 2; // On the second value, we determine if the report is increasing or decreasing
increasing = current > previous;
else; // After the second value, we check if the report keeps increasing or decreasing
if increasing <> (current > previous); // If the current value doesn’t conform to the report direction, the report is unsafe
return *off;
endif;
endif;
if not (%abs(current – previous) in %range(1:3)); // If the difference from the previous value is not between 1 and 3, the report is unsafe
return *off;
endif;
endif;

index += 1; // Increase the index
previous = current; // The current value is stored as the previous value for the next iteration
endfor;

return *on; // If we get here, the report isn’t unsafe, therefore it is safe

end-proc;
Part 2
In part 2, we’ll use the same input as in part 1.
We still need to calculate how many reports are safe but this time, we tolerate, at most, one incorrect level in a report (meaning that by removing one level, we get a report with only correct levels).
We modify the isSafe procedure by adding a second parameter that is the index of the level we want to skip. If this index is 0, it means that we don’t skip any level.
When we encounter an incorrect level, instead of declaring the report as unsafe, we call the isSafe procedure again but skipping the current index (and the n-1 and n-2 in some cases) if we are not already skipping a level. If by skipping a level, the report is safe then we consider the report safe.
**free
ctl-opt dftactgrp(*no);

dcl-pi *n;
input char(50);
end-pi;

dcl-pr isSafe ind;
data varchar(255);
skip int(10) value;
end-pr;

dcl-s fileName varchar(50);

dcl-s data varchar(255);
dcl-s safeReports int(10) inz(0);

fileName = %trim(input);

exec sql declare c1 cursor for select cast(line as varchar(255)) from table(qsys2.ifs_read_utf8(path_name => :fileName));

exec sql open c1;

exec sql fetch from c1 into :data;

dow sqlcode = 0;

if isSafe(data:0);
safeReports += 1;
endif;

exec sql fetch from c1 into :data;
enddo;

exec sql close c1;

snd-msg *info ‘Safe reports: ‘ + %char(safeReports) %target(*pgmbdy:1);

*inlr = *on;
return;

dcl-proc isSafe;

dcl-pi *n ind;
data varchar(255);
skip int(10) value; // if skip is 0, there is no skip. If skip > 0, this is the index to skip. Passed by value for ease of calling
end-pi;

dcl-s value char(5);
dcl-s previous int(10);
dcl-s current int(10);
dcl-s index int(10) inz(1);
dcl-s realIndex int(10) inz(1); // This is the real index that doesn’t take skipping into account
dcl-s increasing ind;

for-each value in %split(data);
if realIndex <> skip; // If the current index is the one to be skipped, then we skip
current = %int(value);

if index > 1;
if index = 2;
increasing = current > previous;
else;
if increasing <> (current > previous);
if skip > 0; // If we are already skipping and still have a bad level, the report is unsafe since the Problem Dampener can only tolerate one bad level
return *off;
else;
if index = 3; // The 3rd value is a special case, the bad level can be fixed by removing the first, second or third value
return isSafe(data:3) or isSafe(data:2) or isSafe(data:1);
else; // After the third value, the direction can only be fixed by removing the current value
return isSafe(data:index);
endif;
endif;
endif;
endif;
if not (%abs(current – previous) in %range(1:3));
if skip >0; // If we are already skipping and still have a bad level, the report is unsafe since the Problem Dampener can only tolerate one bad level
return *off;
else; // We try to fix the report by removing the current or the previous value
return isSafe(data:index) or isSafe(data:index-1);
endif;
endif;
endif;

index += 1;
previous = current;
endif;
realIndex += 1; // realIndex is incremented even if we are skipping this value
endfor;

return *on;

end-proc;”}]] Read More 

Overview of VS code for IBMi Programmers IO

​In this video we will discover how to use Visual Studio Code for IBMi and benefits of using Visual Studio Code for IBMi.Programmers.io is a registered IBM business partner and a global IT services and consulting firm, providing on-demand software development Read More 

Advent of Code 2024 day 1, RPG edition

​[[{“value”:”Advent of Code is an advent calendar of small programming puzzles.
Each day has two parts. The second part is unlocked when you solve the first part.
I will solve the first day puzzle using RPG.
The puzzle as well as the input data are available here.
The code is available here.
Part 1
We are provided with a (stream) file containing 2 lists of numbers side by side.
We need to pair the numbers from each list in ascending order. For each pair, we define the distance as the difference between both elements of the pair (as a positive value or 0).
We must calculate the total distance between the lists, which is the sum of the distances of each pair.
We can download the file from the AOC website and store it in the IFS.
The IFS file can easily be read using QSYS2.IFS_READ_UTF8 table function.
Each line contains two five digits numbers starting on column 1 and 9. Each number can be extracted with the substring function.
We can create a first SQL cursor reading and sorting the first list and a second cursor doing the same for the second list.
We can the read from both cursors and calculate the distance for each pair and sum this distance to compute the total distance.
We can then send the information to the user with the snd-msg opcode.
Here is the RPG code for part 1:
**free

// One parameter: the path to the input file
dcl-pi *n;
input char(50);
end-pi;

dcl-s fileName varchar(50); // ifs_read_utf8 doesn’t like trailing spaces in the file name, so we use a VARCHAR to get rid of them
dcl-s val1 int(10); // will hold the current value from the first list
dcl-s val2 int(10); // will hold the current value from the second list
dcl-s diff int(20) inz(0); // will hole the distance between the lists

fileName = %trim(input);

// Cursor for the first list
exec sql declare c1 cursor for select integer(substring(line, 1, 5)) v
from table(qsys2.ifs_read_utf8(path_name => :fileName))
order by v;

// Cursor for the second list
exec sql declare c2 cursor for select integer(substring(line, 9, 5)) v
from table(qsys2.ifs_read_utf8(path_name => :fileName))
order by v;

exec sql open c1;
exec sql open c2;

// Read the first pair
exec sql fetch from c1 into :val1;
exec sql fetch from c2 into :val2;

dow sqlcode = 0;
diff += %abs(val1-val2); // distance is the absolute value of the difference

// Read the next pair.
exec sql fetch from c1 into :val1;
exec sql fetch from c2 into :val2;
enddo;

exec sql close c1;
exec sql close c2;

// Give the answer to the user
snd-msg *info ‘Distance is ‘+%char(diff) %target(*pgmbdy:1);

*inlr = *on;
return;
Part 2
In part 2, we’ll use the same input as in part 1.
This time, we’ll calculate the similarity score: for each element of the first list, the similarity score is the value of the element multiplied by the number of times it appears in the second list.
This can be done with a single SQL instruction, here is the full RPG code:
**free

// One parameter: the path to the input file
dcl-pi *n;
input char(50);
end-pi;

dcl-s fileName varchar(50); // ifs_read_utf8 doesn’t like trailing spaces in the file name, so we use a VARCHAR to get rid of them
dcl-s sim int(20) inz(0); // Will contain the similarity score

fileName = %trim(input);

// We use SELECT INTO since we’re reading a single value
exec sql select sum(integer(substring(a.line, 1, 5)) * (select
count(*) from table(qsys2.ifs_read_utf8(path_name => :fileName)) b
where substring(b.line, 9, 5) = substring(a.line, 1, 5))) into :sim
from table(qsys2.ifs_read_utf8(path_name => :fileName)) a;

// Give the answer to the user
snd-msg *info ‘Similarity score is ‘+%char(sim) %target(*pgmbdy:1);

*inlr = *on;
return;”}]] Read More 

Verified by MonsterInsights