Tracing Application Engines

Tracing particular Process Definitions
This SQL script can make things a little clearer.

select '-TRACE ' || (
1    + -- Initiate the Application Engine Step trace
2    + -- Initiate the Application Engine SQL trace
4    + -- Trace dedicated Temp Table Allocation to AET file
128  + -- Initiate the Statement Timings trace to file, which is similar to the COBOL timings trace to file
256  + -- Initiate the PeopleCode Detail to the file for the Timings trace
1024 + -- Initiate the Statement Timings trace, but, instead of writing to the trace file, this trace stores the results in the following tables: PS_BAT_TIMINGS_LOG and PS_BAT_TIMINGS_DTL
2048 + -- Adding this value requests a database optimizer trace file
4096 + -- Request a database optimizer to be inserted in the Explain Plan Table of the current database
8192   -- This value sets a trace for Integration Broker transform programs
) as TRACE from dual;

select '-TOOLSTRACESQL ' || (
1    + -- Trace SQL statements
2    + -- Trace SQL statement variables
4    + -- Trace SQL connect, disconnect, commit and rollback
8    + -- Show fetched rows (indicates that it occurred, not data)
16   + -- Show all other API calls except ssb
32   + -- Set Select Buffers (identifies the attributes of columns to be selected).
64   + -- Show database API specific calls
128  + -- Show COBOL statement timings
256  + -- Show Sybase bind information
512  + -- Show Sybase fetch information
4096 + -- Show manager information
8192   -- Show Mapcore information
) as TOOLSTRACESQL from dual;

select '-TOOLSTRACEPC ' || (
1    + -- Trace instructions
2    + -- List the program
4    + -- Show assignments to variables
8    + -- Show fetched values
16   + -- Show stack
64   + -- Trace start of programs
128  + -- Trace external function calls
256  + -- Trace internal function calls
512  + -- Show parameter values
1024 + -- Show function return values
2048   -- Trace each statement in program
) as TOOLSTRACEPC from dual;

Tracing on the Server:
./psft/pt/8.52/appserv/*/psappsrv.cfg is where you can set tracing for Batch server instances.

[Trace]
;=========================================================================
; Trace settings
;=========================================================================
; SQL Tracing Bitfield
;
; Bit       Type of tracing
; ---       ---------------
; 1         - SQL statements
; 2         - SQL statement variables
; 4         - SQL connect, disconnect, commit and rollback
; 8         - Row Fetch (indicates that it occurred, not data)
; 16        - All other API calls except ssb
; 32        - Set Select Buffers (identifies the attributes of columns
;             to be selected).
; 64        - Database API specific calls
; 128       - COBOL statement timings
; 256       - Sybase Bind information
; 512       - Sybase Fetch information
; 1024      - SQL Informational Trace
; Dynamic change allowed for TraceSql and TraceSqlMask
TraceSQL=0

;-------------------------------------------------------------------------
; PeopleCode Tracing Bitfield
;
; Bit       Type of tracing
; ---       ---------------
; 1         - Trace Evaluator instructions  (not recommended)
; 2         - List Evaluator program        (not recommended)
; 4         - Show assignments to variables
; 8         - Show fetched values
; 16        - Show stack
; 64        - Trace start of programs
; 128       - Trace external function calls
; 256       - Trace internal function calls
; 512       - Show parameter values
; 1024      - Show function return value
; 2048      - Trace each statement in program  (recommended)
; Dynamic change allowed for TracePC
TracePC=0

;-------------------------------------------------------------------------
; AE Tracing Bitfield
;
; Bit       Type of tracing
; ---       ---------------
; 1         - Trace STEP execution sequence to AET file
; 2         - Trace Application SQL statements to AET file
; 4         - Trace Dedicated Temp Table Allocation to AET file
; 8         - not yet allocated
; 16        - not yet allocated
; 32        - not yet allocated
; 64        - not yet allocated
; 128       - Timings Report to AET file
; 256       - Method/BuiltIn detail instead of summary in AET Timings Report
; 512       - not yet allocated
; 1024      - Timings Report to tables
; 2048      - DB optimizer trace to file
; 4096      - DB optimizer trace to tables
TraceAE=0

;------------------------------------------------------------------------
; Performance Monitor Tracing Bitfield
;
; Bit       Type of tracing
; ---       ---------------
; 1         - Trace All performance monitor agent activity
TracePPM=0

Portal Navigation Path - Oracle PL/SQL Function

Thanks to Jake Gowens a.k.a. Zapp Brannigan

FUNCTION navString (prtlName IN CHAR, prtlObjName IN CHAR) RETURN VARCHAR2 IS

  prtlLabel         VARCHAR2(30);
  prtlParent        VARCHAR2(30);
  result            VARCHAR2(255);

BEGIN

  prtlLabel := '';

  SELECT MAX(PORTAL_LABEL), MAX(PORTAL_PRNTOBJNAME)
    INTO prtlLabel, prtlParent
    FROM PSPRSMDEFN
   WHERE PORTAL_NAME = prtlName
     AND PORTAL_OBJNAME = prtlObjName
     AND PORTAL_REFTYPE = 'C';

  if prtlLabel IS NOT NULL then
    if prtlParent = 'PORTAL_ROOT_OBJECT' then
      result := prtlLabel;
    else
      result := navStringF(prtlName, prtlParent) || ' > ' || prtlLabel;
    end if;
  end if;

  return result;

END navString;

Example:
Select navString(a.portal_name, a.portal_objname)
From psprsmdefn a
Where portal_uri_seg2 = 'JOB_DATA';

Returns:
Workforce Administration > Job Information > Job Data

Automatically Generate Upgrade Scripts

Snippet of PeopleCode that takes a Project and creates an upgrade script for all the Records in that Project. Does 90%+ of the work for you.

Requires a Prompt to select the Project, a button to put this code under, and a HTML Area to output the results to.

If None(NS_MN_TEST.PROJECTNAME.Value) Then
   MessageBox(0, "", 0, 0, "Enter a Project Name");
Else
   
   Local string &title, &result, &fields;
   Local integer &f;
   
   Local Record &recPSPROJECTDEFN = CreateRecord(Record.PSPROJECTDEFN);
   &recPSPROJECTDEFN.PROJECTNAME.Value = NS_MN_TEST.PROJECTNAME.Value;
   &recPSPROJECTDEFN.SelectByKey();
   
   Local Record &recPSOPRDEFN = CreateRecord(Record.PSOPRDEFN);
   &recPSOPRDEFN.OPRID.Value = &recPSPROJECTDEFN.LASTUPDOPRID.Value;
   &recPSOPRDEFN.SelectByKey();
   
   &title = &title | "/*" | Char(13);
   &title = &title | "     WORK UNIT:     " | &recPSPROJECTDEFN.PROJECTNAME.Value | Char(13);
   &title = &title | "     DEVELOPER:     " | &recPSOPRDEFN.OPRDEFNDESC.Value | Char(13);
   &title = &title | "     DATE:          " | String(%Date) | Char(13);
   &title = &title | "     DESCRIPTION:   " | &recPSPROJECTDEFN.PROJECTDESCR.Value | Char(13);
   &title = &title | "     INSTRUCTIONS:  EXECUTE IN THE TARGET" | Char(13);
   &title = &title | "     DEPENDENCIES:  " | Char(13);
   &title = &title | "*/" | Char(13) | Char(13);
   
   &title = &title | "-- List of Records that have data converted by this script:" | Char(13);
   
   
   Local SQL &sql = CreateSQL("SELECT OBJECTVALUE1 FROM PSPROJECTITEM A, PSRECDEFN B WHERE A.PROJECTNAME = :1 AND A.OBJECTTYPE = 0 AND A.OBJECTID2 = 0 AND B.RECNAME = A.OBJECTVALUE1 AND B.RECTYPE = 0", NS_MN_TEST.PROJECTNAME.Value);
   
   Local string &recName;
   While &sql.Fetch(&recName)
      
      If Left(&recName, 3) = "NS_" Then
         
         &title = &title | "--    " | &recName | Char(13);
         
         &result = &result | Char(13) | "-- " | &recName | Char(13);
         &result = &result | "Delete From ps_" | &recName | ";" | Char(13);
         Local Record &rec = CreateRecord(@("record." | &recName));
         &result = &result | "Insert Into ps_" | &recName | "(" | Char(13);
         For &f = 1 To &rec.FieldCount
            If &f = 1 Then
               &fields = "  " | &rec.GetField(&f).Name | Char(13);
            Else
               &fields = &fields | ", " | &rec.GetField(&f).Name | Char(13);
            End-If;
         End-For;
         &result = &result | &fields | ")" | Char(13);
         &result = &result | "Select " | Char(13) | &fields | "From ps_" | &recName | "@nsconv.unsw.edu.au;" | Char(13);
         
      End-If;
      
   End-While;
   
   &title = &title | Char(13) | "SPOOL ON;" | Char(13);
   &title = &title | "SET ECHO ON;" | Char(13);
   &title = &title | "SPOOL 'c:\temp\" | Substring(&recPSPROJECTDEFN.PROJECTNAME.Value, 4, 7) | "_data_conversion.log' REPLACE;" | Char(13);
   
   
   NS_MN_TEST.HTMLAREA1.Value = &title | Char(13) | Char(13) | &result | Char(13) | Char(13) | "commit;" | Char(13) | Char(13) | "SPOOL OFF;";
   
End-If;
Paste your text here.


Searching Through Views

If you make a change on a Record or View you should check its impact. App Designer doesn't search through SQL definitions but you can do that yourself in SQL:

select SQLID from PSSQLTEXTDEFN
where sqltext like '%SOME_TABLE%'

Posting CommGen to Report Manager

As delivered Communication Generation (CommGen) produces the final, merged output of letters on the Server. There is no way to configure it to send to Report Manager or Process Monitor.

However, there is an easy fix.

Each Process Instance has its own directory made at run-time, and all files in that directory are published to Process Monitor and Report Manager. We just need to make a simple tweak to create the Merged File Output in the process's Process Instance directory instead of the file destination directory as defined on the Run Control (by the way it would pay to modify the run control accordingly as well; hide the now unused File Output field etc.).

Application Package SCC_COMMGEN:Model:CommGen


/* This method merges all the communications that have been generated so far */
method MergeCommunications

...

   rem &sFileDestination = &rsRunCtlParms(1).SCC_CG_RUNCTL.FILE_PATH.Value;
   &sFileDestination = "log_output/AE_SCC_COMMGEN_" | &ProcessInstance;

Just change the one line, as above. This will have the merged file go to Report Manager and Process Monitor instead of a directory on the server. I suspect this issue may be a left over from version 7, where users could run on the client and have it appear on their local drive.

Rowset to HTML Table

Another possibly useless tool:

<*--------------------------------------------------------------------------

 UNSW WUHR005 Michael Nitschke 12/11/2012
 Utility to take a rowset and return all its values.
 At time of writing only handles a single record, flat rowset.

 Example:

   import NS_NEXTGEN:Utilities:RowsetToHTML;

   /* Create and fill a rowset. */
   Local Rowset &rowset = CreateRowset(Record.SOME_RECORD);
   &rowset.Fill();

   /* Put the rowset's data into an HTML Area on a Page. */
   Local NS_NEXTGEN:Utilities:RowsetToHTML &rs2HTML = create NS_NEXTGEN:Utilities:RowsetToHTML(&rowset);
   NS_DERIVED.HTML_AREA_01.Value = &rs2HTML.RowsetAsHTML;

 The above will populate a HTML Area on a page with the values of the rowset.
 This utility could be extended to include heirarchical data.

----------------------------------------------------------------------------*>

class RowsetToHTML
   method RowsetToHTML(&rs As Rowset);
   property string RowsetAsHTML;
end-class;

method RowsetToHTML
   /+ &rs as Rowset +/
   
   Local string &style;
   &style = &style | "<style type=""text/css"">";
   &style = &style | "table.rowset {";
   &style = &style | "    border-width: 3px;";
   &style = &style | "    border-spacing: 0px;";
   &style = &style | "    border-style: solid;";
   &style = &style | "    border-color: black;";
   &style = &style | "    border-collapse: collapse;";
   &style = &style | "    background-color: white;";
   &style = &style | "}";
   &style = &style | "table.rowset th {";
   &style = &style | "    border-width: 1px;";
   &style = &style | "    padding: 4px;";
   &style = &style | "    border-style: dotted;";
   &style = &style | "    border-color: gray;";
   &style = &style | "    background-color: rgb(200, 200, 200);";
   &style = &style | "    -moz-border-radius: ;";
   &style = &style | "    font-size: small ;";
   &style = &style | "}";
   &style = &style | "table.rowset th.recname {";
   &style = &style | "    background-color: rgb(0, 0, 0);";
   &style = &style | "    color: white;";
   &style = &style | "    font-size: large;";
   &style = &style | "    font-weight: normal;";
   &style = &style | "    text-align: left;";
   &style = &style | "}";
   &style = &style | "table.rowset tr.r0 td {";
   &style = &style | "    background-color: rgb(240, 240, 240);";
   &style = &style | "}";
   &style = &style | "table.rowset tr.r1 td {";
   &style = &style | "    background-color: rgb(250, 250, 250);";
   &style = &style | "}";
   &style = &style | "table.rowset td {";
   &style = &style | "    border-width: 1px;";
   &style = &style | "    padding: 4px;";
   &style = &style | "    border-style: dotted;";
   &style = &style | "    border-color: gray;";
   &style = &style | "    background-color: white;";
   &style = &style | "    -moz-border-radius: ;";
   &style = &style | "    font-size: small ;";
   &style = &style | "}";
   &style = &style | "</style>";
   
   Local string &html;
   &html = &html | "<table class=""rowset"">";
   &html = &html | "<tr>";
   &html = &html | "<th class=""recname"" colspan=""" | (&rs(1).GetRecord(1).FieldCount + 1) | """>" | &rs(1).GetRecord(1).Name | "</th>";
   &html = &html | "</tr>";
   &html = &html | "<tr>";
   Local integer &f;
   &html = &html | "<th>row</th>";
   For &f = 1 To &rs(1).GetRecord(1).FieldCount
      &html = &html | "<th>" | &rs(1).GetRecord(1).GetField(&f).Name | "</th>";
   End-For;
   &html = &html | "</tr>";
   Local string &alternate = "1";
   Local integer &i;
   For &i = 1 To &rs.RowCount
      If &alternate = "1" Then
         &alternate = "0";
      Else
         &alternate = "1";
      End-If;
      &html = &html | "<tr class=""r" | &alternate | """>";
      &html = &html | "<td>" | &i | "</td>";
      For &f = 1 To &rs(&i).GetRecord(1).FieldCount
         &html = &html | "<td>" | &rs(&i).GetRecord(1).GetField(&f).Value | "</td>";
      End-For;
      &html = &html | "</tr>";
   End-For;
   &html = &html | "</table>";
   
   %This.RowsetAsHTML = &style | &html;
   
end-method;

Modifying v7 SQL that uses Variable Data

While upgrading (if there are any more upgrades) you'll eventually encounter a piece of SQL that filters on, say PS_PERSON_COMMUNICATION.ADMIN_FUNCTION and ACAD_CAREER. The latter is now on many child tables; VAR_DATA_XXXX.

For example:

select
  to_char(a.comment_dttm,'dd/mm/yyyy hh24:mi:ss')
, a.cmnt_category
, a.deptid
, a.cmnt_id
, a.comment_dt
, a.comments
from
  ps_person_comment a
where a.emplid = :1
and a.admin_function = :4
and a.acad_career=:2
and a.stdnt_car_nbr = :3
;

This can be resolved to something like the following, where the subselect/temp-table/on-the-fly thingies there are populated by a union between all VAR_DATA_XXXX tables that use ACAD_CAREER and STDNT_CAR_NBR

select
  to_char(a.comment_dttm,'dd/mm/yyyy hh24:mi:ss')
, a.cmnt_category
, a.deptid
, a.cmnt_id
, a.comment_dt
, a.comments

from
  ps_person_comment a
,
    (select 'ADMA' as admin_function, common_id, var_data_seq, acad_career, stdnt_car_nbr from ps_VAR_DATA_ADMA
    union
    select 'ADMP' as admin_function, common_id, var_data_seq, acad_career, stdnt_car_nbr from ps_VAR_DATA_ADMP
    union
    select 'CASN' as admin_function, common_id, var_data_seq, acad_career, stdnt_car_nbr from ps_VAR_DATA_CASN
    union
    select 'IPT1' as admin_function, common_id, var_data_seq, acad_career, stdnt_car_nbr from ps_VAR_DATA_IPT1
    union
    select 'SPRG' as admin_function, common_id, var_data_seq, acad_career, stdnt_car_nbr from ps_VAR_DATA_SPRG
    union
    select 'THES' as admin_function, common_id, var_data_seq, acad_career, stdnt_car_nbr from ps_VAR_DATA_THES) x

where a.common_id = &1
and a.admin_function = &4
and a.cmnt_id = b.emplid
and x.admin_function = a.admin_function
and x.common_id = a.common_id
and x.var_data_seq = a.var_data_seq
and x.acad_career = &2
and x.stdnt_car_nbr = &3
;