Last active
February 11, 2025 14:41
-
-
Save forstie/6fa66b0f635b75e3187c2a77b55e55d5 to your computer and use it in GitHub Desktop.
The request... fill an IBM i (SQL) Services gap in Work Management via the creation of a CLASS_INFO service.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- | |
-- Subject: Finding IBM i class objects and return the class attributes | |
-- Author: Scott Forstie | |
-- Date : February, 2025 | |
-- | |
-- This service overcomes the lack of OUTFILE support on the Display Class (DSPCLS) CL command | |
-- by copying the spooled file into a Db2 for i table in the SESSION schema. | |
-- | |
-- Features Used : This Gist uses object_statistics, output_queue_entries_basic, SQL PL, Pipe | |
-- | |
-- Resources: | |
-- https://www.ibm.com/docs/api/v1/content/ssw_ibm_i_75/cl/dspcls.htm | |
-- https://www.ibm.com/docs/api/v1/content/ssw_ibm_i_75/cl/cpysplf.htm | |
-- https://www.ibm.com/docs/api/v1/content/ssw_ibm_i_75/cl/dltsplf.htm | |
-- | |
drop function if exists coolstuff.class_info; | |
-- | |
-- UDTF... find class information | |
-- | |
CREATE OR REPLACE FUNCTION coolstuff.class_info () | |
RETURNS TABLE ( | |
class_library VARCHAR(10) FOR SBCS DATA, | |
class_name VARCHAR(10) FOR SBCS DATA, | |
text_description VARCHAR(50) FOR SBCS DATA, | |
last_used date, | |
use_count INTEGER, | |
run_priority INTEGER, | |
time_slice INTEGER, | |
default_wait VARCHAR(11) FOR SBCS DATA, | |
maximum_cpu_time VARCHAR(11) FOR SBCS DATA, | |
maximum_temporary_storage_allowed VARCHAR(11) FOR SBCS DATA, | |
maximum_active_threads VARCHAR(11) FOR SBCS DATA | |
) | |
NOT DETERMINISTIC | |
EXTERNAL ACTION | |
MODIFIES SQL DATA | |
NOT FENCED | |
SET OPTION commit = *none, usrprf = *user, dynusrprf = *user | |
BEGIN | |
DECLARE v_print_line CHAR(133); | |
DECLARE local_sqlcode INTEGER; | |
DECLARE local_sqlstate CHAR(5); | |
DECLARE v_message_text VARCHAR(70); | |
DECLARE v_dspcls VARCHAR(1000); | |
-- | |
-- DSPCLS detail | |
-- | |
DECLARE v_class CHAR(10); | |
DECLARE v_class_library CHAR(10); | |
DECLARE v_class_run_priority INTEGER; | |
DECLARE v_class_time_slice INTEGER; | |
DECLARE v_class_dft_wait VARCHAR(11); | |
DECLARE v_class_max_cpu VARCHAR(11) FOR SBCS DATA; | |
DECLARE v_class_max_tmp_stg VARCHAR(11) FOR SBCS DATA; | |
DECLARE v_class_max_threads VARCHAR(11) FOR SBCS DATA; | |
-- | |
-- OBJECT_STATISTICS detail | |
-- | |
DECLARE find_classes_query_text VARCHAR(5000); | |
DECLARE v_cls_text VARCHAR(50); | |
DECLARE v_job_name VARCHAR(28); | |
DECLARE v_cls_last_use DATE; | |
DECLARE v_cls_use_count INTEGER; | |
DECLARE c_find_classes CURSOR FOR find_classes_query; | |
DECLARE c_find_dspcls_output CURSOR FOR | |
SELECT job_name | |
FROM qsys2.output_queue_entries_basic | |
WHERE user_name = session_user | |
AND spooled_file_name = 'QPDSPCLS' | |
ORDER BY create_timestamp DESC | |
LIMIT 1; | |
DECLARE c_dspcls_output CURSOR FOR | |
SELECT c1 | |
FROM session.splf x | |
WHERE RRN(x) > 4 | |
ORDER BY RRN(x); | |
DECLARE CONTINUE HANDLER FOR sqlexception | |
BEGIN | |
GET DIAGNOSTICS CONDITION 1 | |
local_sqlcode = db2_returned_sqlcode, | |
local_sqlstate = returned_sqlstate; | |
SET v_message_text = 'systools.class_info() failed with: ' CONCAT | |
local_sqlcode CONCAT ' AND ' CONCAT local_sqlstate; | |
SIGNAL SQLSTATE 'QPC01' SET MESSAGE_TEXT = v_message_text; | |
END; | |
DECLARE GLOBAL TEMPORARY TABLE splf (c1 CHAR(133)) WITH replace; | |
SET find_classes_query_text = | |
'select libs.objname, objs.OBJNAME, objs.OBJTEXT, objs.LAST_USED_TIMESTAMP, objs.DAYS_USED_COUNT from table ( | |
qsys2.object_statistics(''QSYS '', ''*LIB'') | |
) libs, lateral ( select * FROM TABLE (qsys2.OBJECT_STATISTICS(libs.objname,''CLS '')) AS a ) objs' | |
; | |
PREPARE find_classes_query FROM find_classes_query_text; | |
OPEN c_find_classes; | |
l1: LOOP | |
FETCH FROM c_find_classes | |
INTO v_class_library, v_class, v_cls_text, v_cls_last_use, | |
v_cls_use_count; | |
GET DIAGNOSTICS CONDITION 1 | |
local_sqlcode = db2_returned_sqlcode, | |
local_sqlstate = returned_sqlstate; | |
IF (local_sqlstate = '02000') THEN | |
CLOSE c_find_classes; | |
RETURN; | |
END IF; | |
SET v_dspcls = 'QSYS/DSPCLS CLS(' CONCAT RTRIM(v_class_library) | |
CONCAT '/' CONCAT RTRIM(v_class) CONCAT | |
') OUTPUT(*PRINT)'; | |
CALL qsys2.qcmdexc(v_dspcls); | |
OPEN c_find_dspcls_output; | |
FETCH FROM c_find_dspcls_output | |
INTO v_job_name; | |
CLOSE c_find_dspcls_output; | |
CALL qsys2.qcmdexc( | |
'QSYS/CPYSPLF FILE(QPDSPCLS) TOFILE(QTEMP/SPLF) SPLNBR(*LAST) JOB(' | |
CONCAT v_job_name CONCAT ') '); | |
OPEN c_dspcls_output; | |
FETCH FROM c_dspcls_output | |
INTO v_print_line; | |
SET v_class_run_priority = INT(SUBSTR(v_print_line, 56, 10)); | |
FETCH FROM c_dspcls_output | |
INTO v_print_line; | |
SET v_class_time_slice = INT(SUBSTR(v_print_line, 56, 10)); | |
FETCH FROM c_dspcls_output | |
INTO v_print_line; | |
FETCH FROM c_dspcls_output | |
INTO v_print_line; | |
SET v_class_dft_wait = SUBSTR(v_print_line, 56, 10); | |
FETCH FROM c_dspcls_output | |
INTO v_print_line; | |
SET v_class_max_cpu = SUBSTR(v_print_line, 56, 10); | |
FETCH FROM c_dspcls_output | |
INTO v_print_line; | |
SET v_class_max_tmp_stg = SUBSTR(v_print_line, 56, 10); | |
FETCH FROM c_dspcls_output | |
INTO v_print_line; | |
SET v_class_max_threads = SUBSTR(v_print_line, 56, 10); | |
CLOSE c_dspcls_output; | |
CALL qsys2.qcmdexc('QSYS/DLTSPLF FILE(QPDSPCLS) SPLNBR(*LAST) JOB(' | |
CONCAT v_job_name CONCAT ') '); | |
PIPE ( | |
v_class_library, | |
v_class, v_cls_text, v_cls_last_use, v_cls_use_count, | |
v_class_run_priority, v_class_time_slice, v_class_dft_wait, | |
v_class_max_cpu, v_class_max_tmp_stg, v_class_max_threads); | |
END LOOP; /* L1 */ | |
CLOSE c_find_classes; | |
END; | |
stop; | |
select * | |
from table ( | |
coolstuff.class_info() | |
); | |
stop; | |
-- | |
-- Create a view to expose the class information | |
-- | |
CREATE OR REPLACE VIEW coolstuff.class_info ( | |
class_library FOR COLUMN class_lib, class_name FOR COLUMN class, | |
TEXT_DESCRIPTION FOR COLUMN text, | |
LAST_USED_TIMESTAMP FOR COLUMN last_used, use_count, | |
run_priority FOR COLUMN priority, time_slice, | |
default_wait FOR COLUMN DFTWAIT, maximum_cpu_time FOR COLUMN cpu_time, | |
maximum_temporary_storage_allowed FOR COLUMN max_stg, | |
maximum_active_threads FOR COLUMN max_thread) AS | |
SELECT * | |
FROM TABLE ( | |
coolstuff.class_info() | |
); | |
stop; | |
-- | |
-- Review the CLASS information | |
-- | |
SELECT * | |
FROM coolstuff.class_info; | |
stop; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Scott.
Nice work as usual - this could be a candidate for a new Work Management service in a future release? π
Allow me to offer a version of this with a twist: Instead of interpreting the printed output from the command DSPCLS, call the QSYS/QWCRCLSI API directly from SQL! π₯
My version is found here: https://gist.github.com/chrjorgensen/4da3562f834abee41b87ab065a2e09f6
It also includes the ELIGIBLE FOR PURGE value in the class object.
Maybe we should spread the word about using API's in SQL? Not many are aware of this possibility, I think. π€
Two different techniques achieving the same goal - offering the programmer a choice... π