AN ORACLE NETWORK TRAVERSAL PL/SQL PROGRAM

Author: Creation Date: Version: Last Updated: Brendan Furey 14 June 2010 1.2 21 January 2012

82007373.doc

.

Page 1 of 37

Table of Contents

Introduction.......................................................................................................4 Hardware/Software Summary......................................................................4 Network Package Structure...............................................................................5 Package Structure Diagram.........................................................................5 Procedure Listing.........................................................................................5 Data Structures...........................................................................................6
Links Array......................................................................................................... 6 Nodes Array.......................................................................................................6 Neighbour Reference Cursor..............................................................................6

Programming Constructs Summary.............................................................6 Algorithm Design...............................................................................................8 Call Structure Diagrams...............................................................................8
Procedure Timing...............................................................................................8 Main Procedures................................................................................................ 9 Any Calling Procedure...................................................................................... 10 Init_Time.......................................................................................................... 10 Increment_Time............................................................................................... 10

Timing Procedures.....................................................................................10

Init Procedure............................................................................................10 Expand Network Procedure.......................................................................10
Extend_One_Level............................................................................................10 Check_Node..................................................................................................... 10 Add_Link.......................................................................................................... 11 Write_Links...................................................................................................... 11 Write_Nodes.....................................................................................................11 Write_Tree....................................................................................................... 11 Write_One_Level.............................................................................................. 11

Write Output Procedure.............................................................................11

Application to Foreign Key Networks...............................................................13 Foreign Key Network 1 – Oracle’s HR Demo Schema.................................13
Output............................................................................................................. 13 Entity Relationship Diagram............................................................................. 14 Network Diagram............................................................................................. 15 Output............................................................................................................. 15 Entity Relationship Diagram............................................................................. 17 Network Diagram............................................................................................. 18

Foreign Key Network 2 – Oracle’s MDSYS Schema (Oracle Spatial)...........15

Application to General Networks.....................................................................19 All Objects Networks..................................................................................19 All Objects Network 1 – 400 Objects Subset..............................................19
Output............................................................................................................. 19 Network Diagram............................................................................................. 21 Output............................................................................................................. 21 Write_Tree....................................................................................................... 23 Open_Node......................................................................................................23

All Objects Network 2 – All Objects............................................................21 Performance Tuning..................................................................................23

PL/SQL Code....................................................................................................24 Network Package.......................................................................................24
Object Types....................................................................................................24 Package........................................................................................................... 24 Object Types and Output Table........................................................................ 30
Page 2 of 37

Network_Node Package.............................................................................30
82007373.doc

Package........................................................................................................... 30

Client SQL..................................................................................................35
Foreign Key Network........................................................................................ 35 Objects Network..............................................................................................36

Change Record
Date 13-Jun-2010 20-Jun-2010 Author BPF BPF Version 1.0 1.1 Change Reference Initial Added performance tuning section, and updated code

82007373.doc

Page 3 of 37

Introduction
This document describes an Oracle PL/SQL program for traversing networks. The program consists of two packages and some object type definitions, and is designed in a modular fashion, so that the code defining a specific network is stored in a separate package body, while the package specification and the traversal package remain independent of specific networks. Oracle programming constructs and performance considerations are highlighted. The original motivation for the program was to provide a listing of Oracle foreign key networks to help in the manual construction of entity relationship diagrams. The document uses two of Oracle’s own schemas to illustrate this usage of the program, and includes diagrams constructed from the output. In order to demonstrate generality, and application to larger networks, an artificial network has been defined on one of Oracle’s internal tables, and the results included here. The algorithm follows a ‘breadth first’ approach, and obtains all links, and the minimum length path for each node. Output is displayed in three sections, with timings. The document includes design specifications and the complete PL/SQL code.

Hardware/Software Summary
Component Database Diagrammer Operating System Computer Description Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production Microsoft Visio 2003 (11.3216.5606) Microsoft Windows 7 Home Premium (32 bit) Samsung X120, 3GB memory, Intel U4100 @ 1.3GHz x 2

82007373.doc

.

Page 4 of 37

Network Package Structure
Package Structure Diagram
In the diagram below, shaded components are those that are specific to a given network, while unshaded components are generic. The diagram does not show body-only procedures, including the timing procedures mentioned later. The driving program (Client) calls an initialisation procedure, Init, in the package Network_Node (having generic specification, specific body) then calls the main generic procedure, Expand_Network, for traversing the network, followed by a procedure to write out the results, Write_Output, both of which are part of the package Network.

Procedure Listing
Field Network Init Expand_Network Write_Output Network Node Init Open_Node Network.Expand_Network Get_Node_Info Write_Log Network.Write_Output Client Performs any initialisation required for the specific network, including a call to the generic version to set field lengths for output Given a node identifier and prior link information, opens a reference cursor that will return the links from the node, except the prior one Gets information (if any) about a node that is not easily obtainable by means of the reference cursor Writes a line of output by any mechanism desired (e.g. to standard output, to a table, or to a file) Called By Network_Node.Init Client Client Description Initialises lengths for node description and info fields Performs the network traversal Lists the network by: network links; nodes; node (extended - i.e. with possible duplicate nodes) tree

82007373.doc

Page 5 of 37

Data Structures
The main data structures are two arrays of package body scope and a reference cursor type in the package specification. The arrays are based on database object types, allowing the use of constructor methods, while the cursor type is based on a PL/SQL record type. Links Array This array corresponds to the links in the network. It is implemented as a PL/SQL nested table. It stores the node that the link leads to, information about the link itself, as well as a pointer to the row in the same array for the link that led to the current link. Field node_id link_level link_index_prior first_child_index link_prefix link_info active_yn Nodes Array This array corresponds to the nodes in the network. It is implemented as a PL/SQL associative (or index-by) array that stores the node information, its level (or number of links from the root node), keyed by the node identifier, as well as a pointer to the row in the links array for the link that led to the node. Field node_level link_index node_desc node_info leaf_yn n_links expand_yn Description Level of the node in relation to the root node, i.e. the (minimum) number of links required to reach it Index of the first (and hence active) record in the links array that led to the node Node description Additional information about the node (e.g. if the node is an Oracle table, the number of records in it) Leaf flag.’ Y’ means there are no higher level nodes linked to the node Number of links to the node Expand flag supplied by the specific package. ‘N’ means the node should not be expanded Description Unique identifier of a node Level of the link in relation to the root node, i.e. the number of links required to reach it Index in links array of link that preceded current link Index in links array of first child link (used for performance reasons) A prefix to be displayed when writing the link (may indicate directionality) Information about the link (e.g. foreign key constraint name in foreign key network) Active flag, ‘N’ means the link leads to a previously encountered node, so do not expand further

Neighbour Reference Cursor This is a strongly typed reference cursor that returns a record for each of the neighbouring arcs of a node, excluding the arc that led to the node. Field node_id node_desc link_prefix link_info Description Unique identifier of a node Node description A prefix to be displayed when writing the link (may indicate directionality for directed networks) Information about the link (e.g. foreign key constraint name in foreign key network)

Programming Constructs Summary
Construct Generic Package Object type Index-by array Nested table array Generic-specific code separation
82007373.doc

Usage The arrays are based on object types (not to be confused with the all_objects table used for an example network later). We use only the default constructor methods. The node array is indexed by the node identifier: that means a node can be checked against the array without searching, to aid performance. The other arrays are of nested table type, but could equally have been varying arrays (VARRAYs). Modular design allows the program to work for any network for which the required specific procedures can be developed and slotted into the specific package.
Page 6 of 37

Reference cursor Recursive procedure call Call timing Specific Package Body Bulk collect Array to table casting Pipelined functions Native dynamic SQL

Using a reference cursor means the generic package can fetch from the database without needing to include the SQL defining the specific network. Recursion is the ideal technique for processing tree structures, and is used in that way to list the network in the form of an ‘extended’ tree; we also use recursion in the network expansion, although ordinary iteration could equally have been used there. Oracle’s timestamp and interval data types (from Oracle 9i on) are used to get accurate timings of procedure calls, allowing us to focus on the right areas for tuning (if necessary). In the highly artificial objects example, opening a node requires a full scan of the object list, and so we collect the list at the start into a nested table array (could equally be a varying array), to scan in memory. In the objects example, the list of links against the node is obtained in an array, and in order to open the reference cursor we use the TABLE keywords to allow selection from the array. An alternative mechanism to the array casting, using a pipelined function, was tried, and turned out to be less efficient (in our Oracle XE system). We would have liked to try it in conjunction with parallel processing but our version did not permit this. In the foreign keys example, we list the number of records in the tables and the last creation dates (where available).

82007373.doc

Page 7 of 37

Algorithm Design
Call Structure Diagrams
Procedure Timing The diagram below shows how individual procedure calls are timed – calls are made just before and just after the procedure to be timed, and a variable is incremented by the second call.

82007373.doc

Page 8 of 37

Main Procedures In the diagram below shading/non-shading indicates specific/generic procedures, which belong respectively to the packages Network_Node and Network. There are two recursive procedures, indicated by the parent-child calls.

82007373.doc

Page 9 of 37

Timing Procedures
Any Calling Procedure A variable, possibly of package body scope, is required for each procedure to be timed, and another one (g_init_time) to save the current time before calls. Initialise variables used for timing Call Init_Time just before any procedure to be timed Call Increment_Time just after any procedure to be timed, passing the variable used for timing that procedure Init_Time Assign current timestamp to variable Increment_Time Subtract variable set in Init_Time from current timestamp and assign result to input/output interval parameter Reset the initialised variable to current timestamp

Init Procedure
Set global variables for the lengths of the node description and information fields

Expand Network Procedure
Call Get_Node_Info to get information about the root node passed in Initialise package arrays Call a recursive function, Extend_One_Level, passing level 0 and start index value of 1 Extend_One_Level If maximum allowed levels exceeded return (with success) Store entry count of the links array plus one as the next start index Loop over the links array from the input starting index to the end of the array If link inactive, skip to next iteration Set the prior link information (use prior node identifier, if no explicit link information returned) Call specific procedure Open_Node to open the cursor for current node Loop over records fetched from cursor Call Check_Node to add link to array, and possibly node End Loop Store the number of links and leaf flag against the node for the current (active) link End Loop If next start index is not greater than current count of links array then Call Extend_One_Level, passing current level plus one as level and next start index as start index End if Check_Node If node exists in node array then -- This check is needed to ensure that we include all links, including multiple links between the same
82007373.doc Page 10 of 37

-- nodes, but that we only include a given link once If [existing node is higher level than current level Or (existing node is at current level and its link index is higher than current index)] then Call Add_Link to add link to links array as inactive End if Else Call specific procedure Get_Node_Info to get node information Call Add_Link to add link to links array as active End if Add_Link Check maximum links not exceeded and raise exception if so Add current link onto end of array If the link was indicated as active then mark the leaf flag variable as ‘N’ If this is the first child of its parent set the parent’s first child pointer to the new record index

Write Output Procedure
Check network found Write network statistics Call the three local (entry-point) procedures listed below, timing each call Write timings Each of the three local entry-point procedures starts by writing a header record, and uses the specific procedure Write_Log to do the writing. Write_Links Loop over links array Obtain prior node (if any) Write link record, blank-padding to indicate level End loop Write_Nodes Check network found Write network statistics and timings Loop over nodes array in alphabetical order (using First and Next methods) Obtain prior node (if any) Write node record End loop Write_Tree Write root node Call Write_One_Level, passing root node as parent and start index of 2 Write_One_Level Loop over links array from start index passed in If the prior node of the current link record is the parent node passed in then If the link is active or we want to include inactive links then Write node record, blank-padding to indicate level
82007373.doc Page 11 of 37

If the link is active then Call Write_One_Level, passing current node as parent and first child link index as start index End if End if Elsif we have found a child previously then Return End if End loop

82007373.doc

Page 12 of 37

Application to Foreign Key Networks
Since version 7 Oracle databases have allowed foreign key relationships between tables to be specified by database constraints. The set of constraints defines a directed network of links between the tables, which can be represented in an entity-relationship diagram. Although it is possible to generate diagrams automatically using tools such as Toad or Microsoft Visio, these are often inferior to manually constructed diagrams. The node identifier is the table name, the node has no further description, the link information is the name of the foreign key, and we use prefixes ‘<’, ‘>’, ‘=’ to denote ‘referenced by’, ‘references’ and ‘selfreferences’, respectively. We have used the network package to list the foreign key networks for two systems supplied by Oracle with Oracle 10g Express Edition. The first is a small demonstration Human Resources schema, while the second is a larger production schema used by the Oracle Spatial application. The sections below show the output produced, and we then go on to show two diagrams constructed manually from the listings using Microsoft Visio. The first is an entity-relationship diagram, with the network traversal superimposed, while the second is a pure network diagram.

Foreign Key Network 1 – Oracle’s HR Demo Schema
This is a simple schema, but with sufficient internal structure to provide a good testing example. It has 10 foreign keys and 7 tables, with a maximum node level of 5, and was listed in 0.112 seconds, with breakdown as shown. The root node was chosen arbitrarily. Output
Total 10 links and 7 nodes: Link Listing (* means repeated node)... Node Id JOBS < EMPLOYEES < JOB_HISTORY < DEPARTMENTS > DEPARTMENTS* = EMPLOYEES* < JOB_HISTORY* > DEPARTMENTS* > LOCATIONS > COUNTRIES > REGIONS Node Listing... Node Id Node Info COUNTRIES [Small - 25; (no creation date)] DEPARTMENTS [Small - 27; (no creation date)] EMPLOYEES [Small - 107; (no creation date)] JOBS [Small - 19; (no creation date)] JOB_HISTORY [Small - 10; (no creation date)] LOCATIONS [Small - 23; (no creation date)] REGIONS [Small - 4; (no creation date)] Node Tree Listing (* means repeated node)... Node JOBS < EMPLOYEES < DEPARTMENTS > LOCATIONS > COUNTRIES > REGIONS > DEPARTMENTS* = EMPLOYEES* < JOB_HISTORY* < JOB_HISTORY > DEPARTMENTS* Times (seconds): Total .112 Open_Node 0 Node_Info .014 Write_Links .001 Write_Nodes 0 Write_Tree .002 Prior Node (any link info) (no prior node) JOBS (emp_job_fk) JOBS (jhist_job_fk) EMPLOYEES (dept_mgr_fk) EMPLOYEES (emp_dept_fk) EMPLOYEES (emp_manager_fk) EMPLOYEES (jhist_emp_fk) JOB_HISTORY (jhist_dept_fk) DEPARTMENTS (dept_loc_fk) LOCATIONS (loc_c_id_fk) COUNTRIES (countr_reg_fk) Lev 4 2 1 0 1 3 5 Link Info emp_job_fk dept_mgr_fk dept_loc_fk loc_c_id_fk countr_reg_fk emp_dept_fk emp_manager_fk jhist_emp_fk jhist_job_fk jhist_dept_fk Prior Node LOCATIONS EMPLOYEES JOBS (no prior node) JOBS DEPARTMENTS COUNTRIES Links 2 4 5 2 3 2 1 Leaf

* *

82007373.doc

Page 13 of 37

Entity Relationship Diagram

82007373.doc

Page 14 of 37

Network Diagram

Foreign Key Network 2 – Oracle’s MDSYS Schema (Oracle Spatial)
This is a more complex schema, and has quite a complicated internal structure. It has 30 foreign keys and 14 tables, with a maximum node level of 4, and was listed in 0.185 seconds, with breakdown as shown. The root node was chosen to be the one with most links. Output
Total 30 links and 14 nodes: Link Listing (* means repeated node)... Node Id SDO_COORD_REF_SYS < SDO_COORD_OPS < SDO_COORD_OPS* > SDO_COORD_OPS* < SDO_COORD_OP_PATHS < SDO_COORD_OP_PATHS* = SDO_COORD_REF_SYS* = SDO_COORD_REF_SYS* 82007373.doc Prior Node (any link info) (no prior node) SDO_COORD_REF_SYS (coord_operation_foreign_source) SDO_COORD_REF_SYS (coord_operation_foreign_target) SDO_COORD_REF_SYS (coord_ref_sys_foreign_proj) SDO_COORD_REF_SYS (coord_op_path_foreign_source) SDO_COORD_REF_SYS (coord_op_path_foreign_target) SDO_COORD_REF_SYS (coord_ref_sys_foreign_geog) SDO_COORD_REF_SYS (coord_ref_sys_foreign_horiz) Page 15 of 37

SDO_COORD_REF_SYS* SDO_COORD_REF_SYS (coord_ref_sys_foreign_legacy) SDO_COORD_REF_SYS* SDO_COORD_REF_SYS (coord_ref_sys_foreign_vert) SDO_COORD_SYS SDO_COORD_REF_SYS (coord_ref_sys_foreign_cs) SDO_DATUMS SDO_COORD_REF_SYS (coord_ref_sys_foreign_datum) = SDO_COORD_OPS* SDO_COORD_OPS (coord_operation_foreign_legacy) > SDO_COORD_OP_METHODS SDO_COORD_OPS (coord_operation_foreign_method) < SDO_COORD_OP_PARAM_VALS SDO_COORD_OPS (coord_op_para_val_foreign_op) < SDO_COORD_AXES SDO_COORD_SYS (coord_axis_foreign_cs) = SDO_DATUMS* SDO_DATUMS (datum_foreign_legacy) > SDO_ELLIPSOIDS SDO_DATUMS (datum_foreign_ellipsoid) > SDO_PRIME_MERIDIANS SDO_DATUMS (datum_foreign_meridian) < SDO_COORD_OP_PARAM_USE SDO_COORD_OP_METHODS (coord_op_para_use_foreign_meth) < SDO_COORD_OP_PARAM_VALS* SDO_COORD_OP_METHODS (coord_op_para_val_foreign_meth) > SDO_COORD_OP_PARAMS SDO_COORD_OP_PARAM_VALS (coord_op_para_val_foreign_para) > SDO_UNITS_OF_MEASURE SDO_COORD_OP_PARAM_VALS (coord_op_para_val_foreign_uom) > SDO_COORD_AXIS_NAMES SDO_COORD_AXES (coord_axis_foreign_axis) > SDO_UNITS_OF_MEASURE* SDO_COORD_AXES (coord_axis_foreign_uom) < SDO_UNITS_OF_MEASURE* SDO_ELLIPSOIDS (ellipsoid_foreign_legacy) > SDO_UNITS_OF_MEASURE* SDO_ELLIPSOIDS (ellipsoid_foreign_uom) > SDO_UNITS_OF_MEASURE* SDO_PRIME_MERIDIANS (prime_meridian_foreign_uom) > SDO_COORD_OP_PARAMS* SDO_COORD_OP_PARAM_USE (coord_op_para_use_foreign_para) = SDO_UNITS_OF_MEASURE* SDO_UNITS_OF_MEASURE (unit_of_measure_foreign_legacy) = SDO_UNITS_OF_MEASURE* SDO_UNITS_OF_MEASURE (unit_of_measure_foreign_uom) Node Listing... Node Id Node Info Lev Prior Node Links SDO_COORD_AXES [Small - 139; (no creation date)] 2 SDO_COORD_SYS 3 SDO_COORD_AXIS_NAMES [Small - 28; (no creation date)] 3 SDO_COORD_AXES 1 SDO_COORD_OPS [Large - 2,244; (no creation date)] 1 SDO_COORD_REF_SYS 6 SDO_COORD_OP_METHODS [Small - 82; (no creation date)] 2 SDO_COORD_OPS 3 SDO_COORD_OP_PARAMS [Small - 153; (no creation date)] 3 SDO_COORD_OP_PARAM_VALS 2 SDO_COORD_OP_PARAM_USE [Small - 680; (no creation date)] 3 SDO_COORD_OP_METHODS 2 SDO_COORD_OP_PARAM_VALS [Large - 9,529; (no creation date)] 2 SDO_COORD_OPS 4 SDO_COORD_OP_PATHS [Small - 365; (no creation date)] 1 SDO_COORD_REF_SYS 2 SDO_COORD_REF_SYS [Large - 4,384; (no creation date)] 0 (no prior node) 11 SDO_COORD_SYS [Small - 65; (no creation date)] 1 SDO_COORD_REF_SYS 2 SDO_DATUMS [Small - 530; (no creation date)] 1 SDO_COORD_REF_SYS 4 SDO_ELLIPSOIDS [Small - 94; (no creation date)] 2 SDO_DATUMS 3 SDO_PRIME_MERIDIANS [Small - 16; (no creation date)] 2 SDO_DATUMS 2 SDO_UNITS_OF_MEASURE [Small - 128; (no creation date)] 3 SDO_COORD_OP_PARAM_VALS 7 Node Tree Listing (* means repeated node)... Node Link Info SDO_COORD_REF_SYS < SDO_COORD_OPS coord_operation_foreign_source = SDO_COORD_OPS* coord_operation_foreign_legacy > SDO_COORD_OP_METHODS coord_operation_foreign_method < SDO_COORD_OP_PARAM_USE coord_op_para_use_foreign_meth > SDO_COORD_OP_PARAMS* coord_op_para_use_foreign_para < SDO_COORD_OP_PARAM_VALS* coord_op_para_val_foreign_meth < SDO_COORD_OP_PARAM_VALS coord_op_para_val_foreign_op > SDO_COORD_OP_PARAMS coord_op_para_val_foreign_para > SDO_UNITS_OF_MEASURE coord_op_para_val_foreign_uom = SDO_UNITS_OF_MEASURE* unit_of_measure_foreign_legacy = SDO_UNITS_OF_MEASURE* unit_of_measure_foreign_uom < SDO_COORD_OPS* coord_operation_foreign_target > SDO_COORD_OPS* coord_ref_sys_foreign_proj < SDO_COORD_OP_PATHS coord_op_path_foreign_source < SDO_COORD_OP_PATHS* coord_op_path_foreign_target = SDO_COORD_REF_SYS* coord_ref_sys_foreign_geog = SDO_COORD_REF_SYS* coord_ref_sys_foreign_horiz = SDO_COORD_REF_SYS* coord_ref_sys_foreign_legacy = SDO_COORD_REF_SYS* coord_ref_sys_foreign_vert > SDO_COORD_SYS coord_ref_sys_foreign_cs < SDO_COORD_AXES coord_axis_foreign_cs > SDO_COORD_AXIS_NAMES coord_axis_foreign_axis > SDO_UNITS_OF_MEASURE* coord_axis_foreign_uom > SDO_DATUMS coord_ref_sys_foreign_datum = SDO_DATUMS* datum_foreign_legacy > SDO_ELLIPSOIDS datum_foreign_ellipsoid < SDO_UNITS_OF_MEASURE* ellipsoid_foreign_legacy > SDO_UNITS_OF_MEASURE* ellipsoid_foreign_uom > SDO_PRIME_MERIDIANS datum_foreign_meridian > SDO_UNITS_OF_MEASURE* prime_meridian_foreign_uom Times (seconds): Total .185 Open_Node .004 Node_Info .004 Write_Links .003 Write_Nodes .001 Write_Tree .003

= = > >

Leaf * * * *

* * *

82007373.doc

Page 16 of 37

Entity Relationship Diagram

82007373.doc

Page 17 of 37

Network Diagram

82007373.doc

Page 18 of 37

Application to General Networks
All Objects Networks
To test the application of the algorithm to larger networks and networks generally, we have defined an undirected network on the standard Oracle table all_objects that stores information about all the different types of database component. In our definition an object is linked to another if its name has the same character as the other in exactly a given number of positions between given start and end positions (note that we ignore objects with shorter names than the end position and we don’t count ‘_’). The examples below use 4, 3 and 12 for the respective given values. The node identifier is the object name plus the (unique) object_id delimited by ‘|’, to make the output more readable, the description is the object type and owner delimited by ‘|’, there is no specific link information as the node pair determines the link completely, and no prefixes are used as the network is undirected.

All Objects Network 1 – 400 Objects Subset
For a smallish example, we specified only objects matching ‘V$%’. Of the 400 matching objects on our database, 19 were in the network defined by the chosen root node, with 32 links, with a maximum node level of 4, and the results were obtained in 0.065 seconds, with breakdown as shown. Output
--- Objects like V$% linked to reference object, using positions 3-12, requiring 4 same : Collected 400 objects*** Root node: V$SESSMETRIC|1484 Total 32 links and 19 nodes: Link Listing (* means repeated node) ... Node Id Prior Node (any link info) V$SESSMETRIC|1484 (no prior node) V$SEGSTAT_NAME|1424 V$SESSMETRIC|1484 V$SESSION_CONNECT_INFO|1273 V$SESSMETRIC|1484 V$SESSION_CURSOR_CACHE|1263 V$SESSMETRIC|1484 V$SESSION_EVENT|1271 V$SESSMETRIC|1484 V$SESSION_LONGOPS|1045 V$SESSMETRIC|1484 V$SESSION_OBJECT_CACHE|1326 V$SESSMETRIC|1484 V$SESSION_WAIT_CLASS|1265 V$SESSMETRIC|1484 V$SESSION_WAIT_HISTORY|1269 V$SESSMETRIC|1484 V$SESSION_WAIT|1267 V$SESSMETRIC|1484 V$SESS_TIME_MODEL|8825 V$SESSMETRIC|1484 V$SES_OPTIMIZER_ENV|939 V$SESSMETRIC|1484 V$SEGMENT_STATISTICS|1422 V$SEGSTAT_NAME|1424 V$SESSION_WAIT_CLASS|1265* V$SEGSTAT_NAME|1424 V$SESSION_WAIT_HISTORY|1269* V$SEGSTAT_NAME|1424 V$SESSION_WAIT|1267* V$SEGSTAT_NAME|1424 V$SESS_TIME_MODEL|8825* V$SESSION_CONNECT_INFO|1273 V$SESS_TIME_MODEL|8825* V$SESSION_CURSOR_CACHE|1263 V$SERVICEMETRIC_HISTORY|1498 V$SESSION_EVENT|1271 V$SERVICEMETRIC|1496 V$SESSION_EVENT|1271 V$SESS_TIME_MODEL|8825* V$SESSION_LONGOPS|1045 V$SESS_TIME_MODEL|8825* V$SESSION_OBJECT_CACHE|1326 V$SESS_TIME_MODEL|8825* V$SESSION_WAIT_CLASS|1265 V$SESS_TIME_MODEL|8825* V$SESSION_WAIT_HISTORY|1269 V$SESS_TIME_MODEL|8825* V$SESSION_WAIT|1267 V$SERVICEMETRIC_HISTORY|1498* V$SESS_TIME_MODEL|8825 V$SERVICEMETRIC|1496* V$SESS_TIME_MODEL|8825 V$SERVICE_STATS|8817 V$SEGMENT_STATISTICS|1422 V$SERV_MOD_ACT_STATS|8813 V$SERVICEMETRIC_HISTORY|1498 V$SERV_MOD_ACT_STATS|8813* V$SERVICEMETRIC|1496 V$SERV_MOD_ACT_STATS|8813* V$SERVICE_STATS|8817 V$SERVICE_EVENT|1476 V$SERV_MOD_ACT_STATS|8813 V$STREAMS_CAPTURE|8098 V$SERV_MOD_ACT_STATS|8813 Node Listing... Node Id Node Description Lev Prior Node V$SEGMENT_STATISTICS|1422 SYNONYM|PUBLIC 2 V$SEGSTAT_NAME|1424 V$SEGSTAT_NAME|1424 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$SERVICEMETRIC_HISTORY|1498 SYNONYM|PUBLIC 2 V$SESSION_EVENT|1271 V$SERVICEMETRIC|1496 SYNONYM|PUBLIC 2 V$SESSION_EVENT|1271 V$SERVICE_EVENT|1476 SYNONYM|PUBLIC 4 V$SERV_MOD_ACT_STATS|8813 V$SERVICE_STATS|8817 SYNONYM|PUBLIC 3 V$SEGMENT_STATISTICS|1422 V$SERV_MOD_ACT_STATS|8813 SYNONYM|PUBLIC 3 V$SERVICEMETRIC_HISTORY|1498 V$SESSION_CONNECT_INFO|1273 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$SESSION_CURSOR_CACHE|1263 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 82007373.doc

Links 2 5 3 3 1 2 5 2 2

Leaf

* * * * *

Page 19 of 37

V$SESSION_EVENT|1271 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$SESSION_LONGOPS|1045 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$SESSION_OBJECT_CACHE|1326 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$SESSION_WAIT_CLASS|1265 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$SESSION_WAIT_HISTORY|1269 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$SESSION_WAIT|1267 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$SESSMETRIC|1484 0 (no prior node) 11 V$SESS_TIME_MODEL|8825 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$SES_OPTIMIZER_ENV|939 SYNONYM|PUBLIC 1 V$SESSMETRIC|1484 V$STREAMS_CAPTURE|8098 SYNONYM|PUBLIC 4 V$SERV_MOD_ACT_STATS|8813 Node Tree Listing (* means repeated node) ... Node Link Info V$SESSMETRIC|1484 V$SEGSTAT_NAME|1424 V$SEGMENT_STATISTICS|1422 V$SERVICE_STATS|8817 V$SERV_MOD_ACT_STATS|8813* V$SESSION_WAIT_CLASS|1265* V$SESSION_WAIT_HISTORY|1269* V$SESSION_WAIT|1267* V$SESSION_CONNECT_INFO|1273 V$SESS_TIME_MODEL|8825* V$SESSION_CURSOR_CACHE|1263 V$SESS_TIME_MODEL|8825* V$SESSION_EVENT|1271 V$SERVICEMETRIC_HISTORY|1498 V$SERV_MOD_ACT_STATS|8813 V$SERVICE_EVENT|1476 V$STREAMS_CAPTURE|8098 V$SERVICEMETRIC|1496 V$SERV_MOD_ACT_STATS|8813* V$SESSION_LONGOPS|1045 V$SESS_TIME_MODEL|8825* V$SESSION_OBJECT_CACHE|1326 V$SESS_TIME_MODEL|8825* V$SESSION_WAIT_CLASS|1265 V$SESS_TIME_MODEL|8825* V$SESSION_WAIT_HISTORY|1269 V$SESS_TIME_MODEL|8825* V$SESSION_WAIT|1267 V$SESS_TIME_MODEL|8825* V$SESS_TIME_MODEL|8825 V$SERVICEMETRIC_HISTORY|1498* V$SERVICEMETRIC|1496* V$SES_OPTIMIZER_ENV|939 Times (seconds): Total .065 Open_Node .048 Node_Info .002 Write_Links .003 Write_Nodes .002 Write_Tree .003

3 2 2 3 3 3 10 1 1

* * * * * * * *

82007373.doc

Page 20 of 37

Network Diagram

All Objects Network 2 – All Objects
For a larger example, we specified all objects. Of the 5,385 objects on our database, 3,637 were in the network defined by the chosen root node, with 28,075 links, with a maximum node level of 8, and the results were obtained in 178 seconds, with timing breakdown as shown, from which we deduce 3 seconds in the generic algorithm. Clearly, the majority of the time comes in opening the node via a full scan of the stored array, and if this were a real application we would focus on tuning this – it isn’t. There is also scope for tuning the Write_Tree procedure, which takes a lot longer than Write_Links. The output listing gives the first and last ten records from each section, and no diagrams were drawn for obvious reasons. This output was produced using the initial versions of the packages, prior to some changes related to performance tuning, as detailed in the next section. The code section gives the latest versions only. Output
--- Objects like % linked to reference object, using positions 3-12, requiring 4 same : Collected 5385 objects *** Root node: V$SESSMETRIC|1484 Total 28075 links and 3637 nodes: Link Listing (* means repeated node) ... Node Id Prior Node (any link info) V$SESSMETRIC|1484 (no prior node) 82007373.doc Page 21 of 37

ALL_SUMMARIES|3553 ALL_SUMMARIES|3554 DBA_SUMMARIES|3558 ST_GEOMETRY_ARRAY|11340 V$SEGSTAT_NAME|1424 V$SESSION_CONNECT_INFO|1273 V$SESSION_CURSOR_CACHE|1263 V$SESSION_EVENT|1271 V$SESSION_LONGOPS|1045 . . . HTMLDB_PLSQL_JOBS|13317* HTMLDB_PLSQL_JOB|13315* DM_SVM_BUILD|9919 WWV_FLOW_UTILITIES|12157 WWV_FLOW_UTILITIES|13264 V$STANDBY_LOG|1009 DRV$ONLINE_PENDING|9845* KU$_DBLINK_T|6537 KU$_DBLINK_VIEW|6538 KU$_TRLINK_VIEW|6543 Node Listing... Node Id Node Description ADD_JOB_HISTORY|12120 PROCEDURE|HR AGGRCENTROID|12063 TYPE|MDSYS AGGRCONVEXHULL|12062 TYPE|MDSYS AGGRLRSCONCAT3D|12061 TYPE|MDSYS AGGRLRSCONCAT|12060 TYPE|MDSYS ALL_ALL_TABLES|2437 VIEW|SYS ALL_ALL_TABLES|2438 SYNONYM|PUBLIC ALL_APPLY_CONFLICT_COLUMNS|8142 VIEW|SYS ALL_APPLY_CONFLICT_COLUMNS|8143 SYNONYM|PUBLIC ALL_APPLY_DML_HANDLERS|8153 VIEW|SYS . . . _ALL_REPPARAMETER_COLUMN|7667 VIEW|SYS _ALL_REPRESOLUTION|7645 VIEW|SYS _ALL_REPSITES_NEW|7843 VIEW|SYS _ALL_REPSITES_NEW|7844 SYNONYM|PUBLIC _ALL_SQLSET_STATEMENTS_ONLY|9262 VIEW|SYS _ALL_SQLSET_STATEMENTS_ONLY|9263 SYNONYM|PUBLIC _ALL_SQLSET_STATEMENTS_PHV|9266 VIEW|SYS _ALL_SQLSET_STATEMENTS_PHV|9267 SYNONYM|PUBLIC _ALL_SQLSET_STATISTICS_ONLY|9264 VIEW|SYS _ALL_SQLSET_STATISTICS_ONLY|9265 SYNONYM|PUBLIC . . . Node Tree Listing (* means repeated node) ... Node V$SESSMETRIC|1484 ALL_SUMMARIES|3553 ALL_SUMDELTA|2646 ALL_SUBPARTITION_TEMPLATES|3425* ALL_SUBPARTITION_TEMPLATES|3426* ALL_SUBPART_COL_STATISTICS|3387* ALL_SUBPART_COL_STATISTICS|3388* ALL_SUBPART_HISTOGRAMS|3393* ALL_SUBPART_HISTOGRAMS|3394* ALL_SUBPART_KEY_COLUMNS|3399* . . . XDB$IMPORT_T|10205* SYSTEM_PRIVILEGE_MAP|313 GV$TEMPORARY_LOBS|1975* GV$TEMP_HISTOGRAM|1902* GV$TEMP_PING|1624* V$SQL_OPTIMIZER_ENV|941* V$SYSMETRIC_HISTORY|1468* V$SYSMETRIC_SUMMARY|1482* V$SYS_OPTIMIZER_ENV|937* V_$TEMPORARY_LOBS|1365* Times (seconds): Total 178 Open_Node 128 Node_Info 0 Write_Links 2 Write_Nodes 0 Write_Tree 45 82007373.doc

V$SESSMETRIC|1484 V$SESSMETRIC|1484 V$SESSMETRIC|1484 V$SESSMETRIC|1484 V$SESSMETRIC|1484 V$SESSMETRIC|1484 V$SESSMETRIC|1484 V$SESSMETRIC|1484 V$SESSMETRIC|1484

HTMLDB_COLLECTION|13314 HTMLDB_COLLECTION|13314 DM_SVM_APPLY|9923 LCR$_ROW_UNIT|8334 LCR$_ROW_UNIT|8334 INSTANCE_NUM|5495 GV$OFFLINE_RANGE|1852 GV$OFFLINE_RANGE|1852 GV$OFFLINE_RANGE|1852 GV$OFFLINE_RANGE|1852 Lev 4 5 4 5 5 4 4 6 6 6 Prior Node GV$TEMP_HISTOGRAM|1902 AGGRCONVEXHULL|12062 V$ARCHIVE_DEST_STATUS|1416 V$TRANSPORTABLE_PLATFORM|3881 V$TRANSPORTABLE_PLATFORM|3881 V$FIXED_TABLE|1257 V$FIXED_TABLE|1257 DBA_APPLICATION_ROLES|5479 DBA_APPLICATION_ROLES|5479 DBA_APPLICATION_ROLES|5479 Links 8 4 20 11 11 17 17 4 4 4 Leaf * * * * * * *

5 5 5 5 6 6 6 6 6 6

ALL_TAB_PARTITIONS|3349 OLAP_EXPRESSION_BOOL|7327 DBMS_REPCAT_ADMIN|7927 DBMS_REPCAT_ADMIN|7927 _ALL_REPSITES_NEW|7843 _ALL_REPSITES_NEW|7843 _ALL_REPSITES_NEW|7843 _ALL_REPSITES_NEW|7843 _ALL_REPSITES_NEW|7843 _ALL_REPSITES_NEW|7843

43 17 44 44 10 10 10 10 10 10

* * * * * * * *

Link Info

Page 22 of 37

Performance Tuning
Write_Tree In the original version of this procedure, the recursive procedure Write_One_Level searched the links array for children of the current node, starting from one position after the current index in the calling instance to the end of the array. Noting that Write_Tree took 45 seconds compared to 2 seconds for Write_Links, we tried to improve the performance by two steps: End position Noting that the children of a node are stored contiguously in the links array, once one child has been found, and then a node if found that is not a child, the search can be terminated. Start position In order to further limit the search, we added the first_child_index element to the links array, and set its value for links that have children only, during the construction of the array. Performance Change After making the changes the network results were identical but the timings differed. We also added a separate timer for the fetch operation, and got the following timings output: Timer Total Open_Node Fetch Node_Info Write_Links Write_Nodes Write_Tree Seconds 146.032 126.569 5.122 .059 6.16 .598 4.508

Open_Node The main time now taken is in the specific Open_Node procedure. In the original version of this procedure (which is still present in the listed code – g_use_pipe = FALSE), the entire objects array is scanned and compared with the current node to obtain a list of linked objects that are stored in another array that is then selected from in the reference cursor by means of the TABLE function. We are not interested in any very specific tuning because of the artificiality of the example, but we wondered what effect using Oracle’s pipelining functionality would have. Firstly, we would no longer need the second array, as we would be piping each row back as soon as it was found. Secondly, it might have been possible to use parallel processing across multiple CPUs by splitting the query into a union across different segments of the array. Unfortunately this possibility could not be explored as Oracle 10g XE will not use more than one CPU. Performance Change After making the changes the network results were identical but the timings differed. Timer Total Open_Node Fetch Node_Info Write_Links Write_Nodes Write_Tree Seconds 242.225 60.618 168.215 .041 4.827 .644 4.654

As can be seen, the performance has deteriorated significantly. While the opening of the cursor has actually improved from 127 down to 61 seconds, the fetching has increased from 5 to 168 seconds.

82007373.doc

Page 23 of 37

PL/SQL Code
Network Package
Object Types
CREATE OR REPLACE TYPE node_type AS OBJECT ( node_level link_index node_desc node_info leaf_yn n_links expand_yn / CREATE OR REPLACE TYPE link_type AS OBJECT ( node_id link_level link_index_prior first_child_index link_prefix link_info active_yn / INTEGER, INTEGER, VARCHAR2(100), VARCHAR2(500), VARCHAR2(1), INTEGER, VARCHAR2(1)) VARCHAR2(100), INTEGER, INTEGER, INTEGER, VARCHAR2(10), VARCHAR2(100), VARCHAR2(1));

Package
CREATE OR REPLACE PACKAGE Network AS /******************************************************************************************************** * Program: Network.pks * Revision: 1.0 * Description: Network package, part of a program for traversal of general networks; this package is the * generic part of the program, and the body requires Network_Node specification to compile, * and its body (which holds the network-specific code) to execute. * See 'An Oracle General Network Traversal PL/SQL Program' * * Created by: Brendan Furey * Created on: 14 June 2010 *********************************************************************************************************/ TYPE neighbour_type IS RECORD ( node_id node_desc link_prefix link_info TYPE curtyp IS VARCHAR2(60), VARCHAR2(100), VARCHAR2(30), VARCHAR2(100)); REF CURSOR RETURN neighbour_type; -- strongly typed

PROCEDURE Init (p_desc_len PLS_INTEGER, p_info_len PLS_INTEGER); FUNCTION Expand_Network ( p_root_node VARCHAR2, p_max_level PLS_INTEGER) RETURN BOOLEAN; PROCEDURE Write_Output (p_include_inactive BOOLEAN DEFAULT TRUE); END Network; / SHO ERR CREATE OR REPLACE PACKAGE BODY Network AS c_dummy_node c_max_links c_max_level_pad c_star c_col_1 g_client_expand Too_Many_Links TYPE node_list_type IS TABLE OF TYPE link_list_type IS TABLE OF g_node_list_null g_node_list g_link_list g_desc_len g_info_len g_start_time g_init_time 82007373.doc CONSTANT VARCHAR2(30) := 'Node with unlikely name|0'; CONSTANT PLS_INTEGER := 200000; CONSTANT PLS_INTEGER := 20; CONSTANT VARCHAR2(1) := '*'; CONSTANT VARCHAR2(1) := ' '; VARCHAR2(1); EXCEPTION; node_type INDEX BY VARCHAR2(40); link_type; node_list_type; node_list_type; link_list_type; PLS_INTEGER := 0; PLS_INTEGER := 0; TIMESTAMP; TIMESTAMP; Page 24 of 37

g_time_open_node g_time_fetch g_time_node_info g_time_write_links g_time_write_nodes g_time_write_tree

INTERVAL INTERVAL INTERVAL INTERVAL INTERVAL INTERVAL

DAY(1) DAY(1) DAY(1) DAY(1) DAY(1) DAY(1)

TO TO TO TO TO TO

SECOND; SECOND; SECOND; SECOND; SECOND; SECOND;

PROCEDURE Init (p_desc_len PLS_INTEGER, p_info_len PLS_INTEGER) IS BEGIN g_desc_len g_info_len g_start_time g_time_open_node g_time_fetch g_time_node_info g_time_write_links g_time_write_nodes g_time_write_tree END Init; PROCEDURE Init_Time IS BEGIN g_init_time := SYSTIMESTAMP; END Init_Time; PROCEDURE Increment_Time (x_time IN OUT INTERVAL DAY TO SECOND) IS BEGIN x_time := x_time + SYSTIMESTAMP - g_init_time; g_init_time := SYSTIMESTAMP; END Increment_Time; FUNCTION Expand_Network ( p_root_node p_max_level l_node_info VARCHAR2(500); VARCHAR2, PLS_INTEGER) RETURN BOOLEAN IS := p_desc_len; := p_info_len; := := := := := := := SYSTIMESTAMP; INTERVAL '0' HOUR; INTERVAL '0' HOUR; INTERVAL '0' HOUR; INTERVAL '0' HOUR; INTERVAL '0' HOUR; INTERVAL '0' HOUR;

FUNCTION Extend_One_Level ( p_level p_link_index_start l_ref_cur l_neighbour l_link_index_start l_prior_link_info l_n_links l_leaf_yn l_is_first_child PROCEDURE Check_Node ( p_node_id p_link_index x_is_first_child l_do_expand PROCEDURE Add_Link (p_active_yn BEGIN curtyp;

PLS_INTEGER, PLS_INTEGER) RETURN BOOLEAN IS neighbour_type; PLS_INTEGER := g_link_list.COUNT + 1; VARCHAR2(60); INTEGER; VARCHAR2(1); BOOLEAN;

VARCHAR2, PLS_INTEGER, IN OUT BOOLEAN) IS BOOLEAN; VARCHAR2) IS

IF g_link_list.COUNT = c_max_links THEN RAISE Too_Many_Links; END IF; g_link_list.Extend; g_link_list (g_link_list.COUNT) := link_type ( p_node_id, p_level+1, p_link_index, NULL, l_neighbour.link_prefix, l_neighbour.link_info, p_active_yn ); IF p_active_yn = 'Y' THEN l_leaf_yn := 'N'; END IF; 82007373.doc Page 25 of 37

IF x_is_first_child THEN g_link_list (p_link_index).first_child_index := g_link_list.COUNT; x_is_first_child := FALSE; END IF; END Add_Link; BEGIN IF g_node_list.Exists(p_node_id) THEN l_node_info := '*' || g_node_list(p_node_id).node_info; g_client_expand := g_node_list(p_node_id).expand_yn;

--- If trial node at same level, then add link if it's not earlier in the list only -- Also, add link if it's at a higher level (meaning part of current iteration -IF ( g_node_list(p_node_id).node_level = p_level AND g_node_list(p_node_id).link_index >= p_link_index ) OR ( g_node_list(p_node_id).node_level > p_level) THEN Add_Link (p_active_yn END IF; RETURN; END IF; Init_Time; Network_Node.Get_Node_Info ( p_node_id => p_node_id, x_node_info => l_node_info, x_expand => g_client_expand); Increment_Time (x_time => g_time_node_info); g_node_list(p_node_id) := node_type ( p_level+1, g_link_list.COUNT+1, l_neighbour.node_desc, l_node_info, 'N', 0, g_client_expand); Add_Link (p_active_yn END Check_Node; BEGIN IF (p_level > p_max_level AND p_max_level != 0) THEN RETURN FALSE; END IF; FOR i IN p_link_index_start..g_link_list.COUNT LOOP IF g_link_list(i).active_yn = 'Y' THEN IF g_link_list(i).link_index_prior = 0 THEN l_prior_link_info := c_dummy_node; ELSE l_prior_link_info := Nvl (g_link_list(i).link_info, g_link_list(g_link_list(i).link_index_prior).node_id); END IF; Init_Time; Network_Node.Open_Node ( p_node_id => g_link_list(i).node_id, p_prior_link_info => l_prior_link_info, x_cur => l_ref_cur); Increment_Time (x_time => g_time_open_node); l_n_links := 1; IF i = 1 THEN l_n_links := 0; END IF; l_leaf_yn := 'Y'; l_is_first_child := TRUE; LOOP Init_Time; FETCH l_ref_cur INTO l_neighbour; Increment_Time (x_time => g_time_fetch); 82007373.doc Page 26 of 37 => g_client_expand); => 'N');

EXIT WHEN l_ref_cur%NOTFOUND; Check_Node ( p_node_id p_link_index x_is_first_child

=> l_neighbour.node_id, => i, => l_is_first_child);

l_n_links := l_n_links + 1; END LOOP; CLOSE l_ref_cur; g_node_list(g_link_list(i).node_id).n_links := l_n_links; g_node_list(g_link_list(i).node_id).leaf_yn := l_leaf_yn; END IF; END LOOP; IF g_link_list.COUNT >= l_link_index_start THEN IF Extend_One_Level ( RETURN TRUE; END IF; END IF; RETURN FALSE; EXCEPTION WHEN Too_Many_Links THEN RETURN TRUE; END Extend_One_Level; BEGIN Init_Time; Network_Node.Get_Node_Info ( p_node_id => p_root_node, x_node_info => l_node_info, x_expand => g_client_expand); Increment_Time (x_time => g_time_node_info); g_node_list := g_node_list_null; g_node_list(p_root_node) := node_type ( 0, 1, NULL, l_node_info, 'N', 0, g_client_expand); g_link_list := link_list_type (link_type ( p_root_node, 0, 0, NULL, NULL, NULL, g_client_expand)); IF Extend_One_Level ( RETURN TRUE; END IF; RETURN FALSE; END Expand_Network; FUNCTION Pad_To_Level (p_level PLS_INTEGER) RETURN VARCHAR2 IS BEGIN IF p_level <= c_max_level_pad THEN RETURN RPad (c_col_1, p_level, ' '); ELSE RETURN c_col_1 || LPad ( '(*** Level ' || p_level || ' ***)' , c_max_level_pad, ' '); END IF; END Pad_To_Level; PROCEDURE Write_Links IS l_padding VARCHAR2(100); 82007373.doc Page 27 of 37 p_level p_link_index_start => 0, => 1) THEN p_level p_link_index_start => p_level + 1, => l_link_index_start) THEN

l_prior_node l_node_id BEGIN

VARCHAR2(60); VARCHAR2(60);

Network_Node.Write_Log ('Link Listing (* means repeated node) ...'); Network_Node.Write_Log (RPad('Node Id', 60) || 'Prior Node (any link info)'); FOR j IN 1..g_link_list.COUNT LOOP IF j = 1 THEN l_prior_node := '(no prior node)'; ELSE l_prior_node := g_link_list(g_link_list(j).link_index_prior).node_id; END IF; Network_Node.Write_Log (RPad(Pad_To_Level (p_level => g_link_list(j).link_level) || g_link_list(j).link_prefix || g_link_list(j).node_id || CASE g_link_list(j).active_yn WHEN 'N' THEN c_star END, 60) || RPad (l_prior_node || CASE WHEN g_link_list(j).link_info IS NOT NULL THEN ' (' || g_link_list(j).link_info || ')' END, 60) || LPad(g_link_list(j).first_child_index, 5) ); END LOOP; END Write_Links; PROCEDURE Write_Nodes IS l_prior_node VARCHAR2(60); l_node_id VARCHAR2(60); BEGIN Network_Node.Write_Log ('Node Listing...'); Network_Node.Write_Log (RPad('Node Id', 40) || CASE WHEN g_desc_len > 0 THEN RPad('Node Description', g_desc_len) END || CASE WHEN g_info_len > 0 THEN RPad('Node Info', g_info_len) END || ' Lev ' || RPad('Prior Node', 40) || 'Links ' || 'Leaf'); l_node_id := g_node_list.First; WHILE l_node_id IS NOT NULL LOOP IF g_node_list(l_node_id).link_index = 1 THEN l_prior_node := '(no prior node)'; ELSE l_prior_node := g_link_list(g_link_list(g_node_list(l_node_id).link_index).link_index_prior).node_id; END IF; Network_Node.Write_Log (RPad(l_node_id, 40) || CASE WHEN g_desc_len > 0 THEN RPad(g_node_list(l_node_id).node_desc, g_desc_len) END || CASE WHEN g_info_len > 0 THEN RPad(g_node_list(l_node_id).node_info, g_info_len) END || LPad(g_node_list(l_node_id).node_level, 4) || ' ' || RPad(l_prior_node, 40) || LPad(g_node_list(l_node_id).n_links, 5) || ' '|| CASE g_node_list(l_node_id).leaf_yn WHEN 'Y' THEN c_star END ); l_node_id := g_node_list.Next(l_node_id); END LOOP; END Write_Nodes; PROCEDURE Write_Tree (p_include_inactive BOOLEAN DEFAULT TRUE) IS PROCEDURE Write_One_Level ( p_node_id p_link_index_start l_found_child BEGIN FOR i IN p_link_index_start..g_link_list.COUNT LOOP IF g_link_list(g_link_list(i).link_index_prior).node_id = p_node_id THEN l_found_child := TRUE; IF g_link_list(i).active_yn = 'Y' OR p_include_inactive THEN Network_Node.Write_Log (RPad ( Pad_To_Level (p_level => g_link_list(i).link_level) || 82007373.doc Page 28 of 37 BOOLEAN := FALSE; VARCHAR2, PLS_INTEGER) IS

g_link_list(i).link_prefix || g_link_list(i).node_id || CASE g_link_list(i).active_yn WHEN 'N' THEN c_star END, 60) || CASE WHEN g_link_list(i).link_info IS NOT NULL THEN g_link_list(i).link_info END); IF g_link_list(i).active_yn = 'Y' AND g_link_list(i).first_child_index IS NOT NULL THEN Write_One_Level ( p_node_id p_link_index_start END IF; END IF; ELSIF l_found_child THEN --- Children are contiguous -RETURN; END IF; END LOOP; END Write_One_Level; BEGIN Network_Node.Write_Log ('Node Tree Listing (* means repeated node) ...'); Network_Node.Write_Log (RPad ('Node', 60) || 'Link Info'); Network_Node.Write_Log (RPad ( Pad_To_Level (p_level => 0) || g_link_list(1).node_id || CASE g_link_list(1).active_yn WHEN 'N' THEN c_star END, 60) || CASE WHEN g_link_list(1).link_info IS NOT NULL THEN g_link_list(1).link_info END); Write_One_Level ( END Write_Tree; PROCEDURE Write_Output (p_include_inactive BOOLEAN DEFAULT TRUE) IS FUNCTION Get_Seconds (p_interval INTERVAL DAY TO SECOND) RETURN NUMBER IS BEGIN RETURN EXTRACT (SECOND FROM p_interval) + 60 * EXTRACT (MINUTE FROM p_interval); END Get_Seconds; BEGIN IF g_link_list IS NULL THEN Network_Node.Write_Log ('No linked nodes found!'); RETURN; END IF; Network_Node.Write_Log ('Total '|| To_Char(g_link_list.COUNT-1) || ' links and ' || g_node_list.COUNT || ' nodes:'); Init_Time; Write_Links; Increment_Time (x_time => g_time_write_links); Write_Nodes; Increment_Time (x_time => g_time_write_nodes); Write_Tree (p_include_inactive => p_include_inactive); Increment_Time (x_time => g_time_write_tree); Network_Node.Write_Log Network_Node.Write_Log Network_Node.Write_Log Network_Node.Write_Log Network_Node.Write_Log Network_Node.Write_Log Network_Node.Write_Log Network_Node.Write_Log END Write_Output; END Network; / SHO ERR ('Times (seconds): '); ('Total ' || Get_Seconds (SYSTIMESTAMP - g_start_time)); ('Open_Node ' || Get_Seconds (g_time_open_node)); ('Fetch ' || Get_Seconds (g_time_fetch)); ('Node_Info ' || Get_Seconds (g_time_node_info)); ('Write_Links ' || Get_Seconds (g_time_write_links)); ('Write_Nodes ' || Get_Seconds (g_time_write_nodes)); ('Write_Tree ' || Get_Seconds (g_time_write_tree)); p_node_id p_link_index_start => g_link_list(1).node_id, => 2); => g_link_list(i).node_id, => g_link_list(i).first_child_index);

82007373.doc

Page 29 of 37

Network_Node Package
Object Types and Output Table
DROP SEQUENCE output_log_s / CREATE SEQUENCE output_log_s / DROP TABLE output_log / CREATE TABLE output_log ( line_ind line_text / DROP TYPE object_list_type; CREATE OR REPLACE TYPE obj_type AS OBJECT (object_id object_name object_type owner / DROP TYPE obj_nbr_list_type; CREATE OR REPLACE TYPE obj_nbr_type AS OBJECT (obj_id obj_desc / CREATE TYPE obj_nbr_list_type / sho err CREATE TYPE object_list_type / sho err

NUMBER, VARCHAR2(255))

NUMBER, VARCHAR2(30), VARCHAR2(30), VARCHAR2(30))

VARCHAR2(100), VARCHAR2(500)); AS TABLE OF obj_nbr_type; AS TABLE OF obj_type

Package
CREATE OR REPLACE PACKAGE Network_Node AS /******************************************************************************************************** * Program: Network_Node.pks * Revision: 1.0 * Description: Network_Node package, part of a program for traversal of general networks; the body holds * the network-specific code for the program; requires Network package specification * to compile. * See 'An Oracle General Network Traversal PL/SQL Program' * * Created by: Brendan Furey * Created on: 14 June 2010 *********************************************************************************************************/ PROCEDURE Write_Log ( p_line VARCHAR2); PROCEDURE Init ( p_obj_criterion VARCHAR2, p_pos_beg PLS_INTEGER, p_pos_end PLS_INTEGER, p_cnt_same_reqd PLS_INTEGER, p_use_pipe BOOLEAN DEFAULT FALSE); PROCEDURE Init ( p_do_expand_empty BOOLEAN); PROCEDURE Open_Node ( p_node_id VARCHAR2, p_prior_link_info VARCHAR2, x_cur OUT Network.curtyp); PROCEDURE Get_Node_Info(p_node_id VARCHAR2, x_node_info OUT VARCHAR2, x_expand OUT VARCHAR2); FUNCTION Nbr_Pipe ( p_object_list object_list_type, p_object_id PLS_INTEGER, p_prior_object_id PLS_INTEGER, p_object_name VARCHAR2) RETURN obj_nbr_list_type PIPELINED; END Network_Node; / SHO ERR CREATE OR REPLACE PACKAGE BODY Network_Node AS c_num_fmt c_empty c_large_threshold c_delimiter c_con_desc_len c_con_info_len c_obj_desc_len c_obj_info_len 82007373.doc CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT VARCHAR2(30) := '999,999,990'; VARCHAR2(10) := '[EMPTY]'; PLS_INTEGER := 1000; VARCHAR2(10) := '|'; PLS_INTEGER := 0; PLS_INTEGER := 40; PLS_INTEGER := 30; PLS_INTEGER := 0; Page 30 of 37

g_net_type g_pos_beg g_pos_end g_cnt_same_reqd g_do_expand_empty g_use_pipe g_object_list PROCEDURE Write_Log (p_line VARCHAR2) IS BEGIN INSERT INTO output_log ( line_ind, line_text ) VALUES ( output_log_s.NEXTVAL, p_line); END Write_Log;

VARCHAR2(30) := 'CON'; PLS_INTEGER; PLS_INTEGER; PLS_INTEGER; BOOLEAN := FALSE; BOOLEAN := FALSE; object_list_type;

PROCEDURE Set_Net_Type (p_net_type VARCHAR2) IS BEGIN g_net_type := p_net_type; END Set_Net_Type; PROCEDURE Init (p_do_expand_empty BOOLEAN) IS BEGIN Set_Net_Type (p_net_type => 'CON'); g_do_expand_empty := p_do_expand_empty; Network.Init (p_desc_len => c_con_desc_len, p_info_len => c_con_info_len); END Init; PROCEDURE Init ( p_obj_criterion p_pos_beg p_pos_end p_cnt_same_reqd p_use_pipe VARCHAR2, PLS_INTEGER, PLS_INTEGER, PLS_INTEGER, BOOLEAN DEFAULT FALSE) IS

BEGIN

Set_Net_Type (p_net_type => 'OBJ'); g_pos_beg := p_pos_beg; g_pos_end := p_pos_end; g_cnt_same_reqd := p_cnt_same_reqd; g_use_pipe := p_use_pipe; SELECT BULK INTO FROM WHERE AND ORDER obj_type(object_id, object_name, object_type, owner) COLLECT g_object_list all_objects obj Length(object_name) >= g_pos_end object_name LIKE p_obj_criterion BY object_id;

Network.Init (p_desc_len => c_obj_desc_len, p_info_len => c_obj_info_len); Write_Log ('--- Objects like ' || p_obj_criterion || ' linked to reference object, using positions ' || p_pos_beg || '-' || p_pos_end || ', requiring ' || p_cnt_same_reqd || ' same :'); Write_Log (p_line => 'Collected ' || g_object_list.COUNT || ' objects'); END Init; PROCEDURE Open_Node_Con ( p_node_id p_prior_link_info x_cur BEGIN VARCHAR2, VARCHAR2, Network.curtyp) IS

OUT

OPEN x_cur FOR SELECT con_t.table_name node_id, NULL node_desc, CASE WHEN con_t.table_name = con_f.table_name THEN '= ' ELSE '> ' END link_prefix, Lower(con_f.constraint_name) link_info FROM all_constraints con_f, all_constraints con_t WHERE con_t.constraint_name = con_f.r_constraint_name AND con_t.owner = con_f.r_owner AND con_f.constraint_type = 'R' AND con_f.table_name = p_node_id AND Lower(con_f.constraint_name) != p_prior_link_info UNION SELECT con_f.table_name, 82007373.doc Page 31 of 37

FROM WHERE AND AND AND AND AND ORDER

NULL, '< ', Lower(con_f.constraint_name) all_constraints all_constraints con_f.r_constraint_name con_t.owner con_f.constraint_type con_t.table_name con_t.table_name Lower(con_f.constraint_name) BY 1, 2;

con_f, con_t = con_t.constraint_name = con_f.r_owner = 'R' = p_node_id != con_f.table_name != p_prior_link_info

END Open_Node_Con; FUNCTION Nbr_Pipe ( p_object_list object_list_type, p_object_id PLS_INTEGER, p_prior_object_id PLS_INTEGER, p_object_name VARCHAR2) RETURN obj_nbr_list_type PIPELINED IS PLS_INTEGER; VARCHAR2(100); VARCHAR2(500);

l_cnt_same l_node_id l_node_desc BEGIN

FOR i IN 1..p_object_list.COUNT LOOP IF p_object_list(i).object_id != p_object_id AND p_object_list(i).object_id != p_prior_object_id THEN l_cnt_same := 0; FOR j IN g_pos_beg..g_pos_end LOOP IF Substr(p_object_list(i).object_name, j, 1) = Substr(p_object_name, j, 1) AND Substr(p_object_list(i).object_name, j, 1) != '_' THEN l_cnt_same := l_cnt_same + 1; END IF; END LOOP; IF l_cnt_same = g_cnt_same_reqd THEN l_node_id := p_object_list(i).object_name || c_delimiter || p_object_list(i).object_id; l_node_desc := p_object_list(i).object_type || c_delimiter || p_object_list(i).owner; PIPE ROW (obj_nbr_type (l_node_id, l_node_desc)); END IF; END IF; END LOOP; END Nbr_Pipe; PROCEDURE Open_Node_Obj_Array ( p_object_id p_prior_object_id p_object_name x_cur l_cnt_same l_obj_nbr_list l_node_id l_node_desc BEGIN PLS_INTEGER, PLS_INTEGER, VARCHAR2, OUT Network.curtyp) IS

PLS_INTEGER; obj_nbr_list_type; VARCHAR2(100); VARCHAR2(500);

FOR i IN 1..g_object_list.COUNT LOOP IF g_object_list(i).object_id != p_object_id AND g_object_list(i).object_id != p_prior_object_id THEN l_cnt_same := 0; FOR j IN g_pos_beg..g_pos_end LOOP IF Substr(g_object_list(i).object_name, j, 1) = Substr(p_object_name, j, 1) AND Substr(g_object_list(i).object_name, j, 1) != '_' THEN l_cnt_same := l_cnt_same + 1; END IF; 82007373.doc Page 32 of 37

END LOOP; IF l_cnt_same = g_cnt_same_reqd THEN l_node_id := g_object_list(i).object_name || c_delimiter || g_object_list(i).object_id; l_node_desc := g_object_list(i).object_type || c_delimiter || g_object_list(i).owner; IF l_obj_nbr_list IS NULL THEN l_obj_nbr_list := obj_nbr_list_type (obj_nbr_type (l_node_id, l_node_desc)); ELSE l_obj_nbr_list.Extend; l_obj_nbr_list (l_obj_nbr_list.COUNT) := obj_nbr_type (l_node_id, l_node_desc); END IF; END IF; END IF; END LOOP; OPEN x_cur FOR SELECT tab.obj_id tab.obj_desc NULL NULL FROM TABLE(l_obj_nbr_list) tab ORDER BY 1; END Open_Node_Obj_Array; PROCEDURE Open_Node_Obj_Pipe ( p_object_id p_prior_object_id p_object_name x_cur BEGIN OPEN x_cur FOR SELECT obj_id obj_desc NULL NULL FROM TABLE (Nbr_Pipe ( PLS_INTEGER, PLS_INTEGER, VARCHAR2, OUT Network.curtyp) IS node_id, node_desc, link_prefix, link_info

node_id, node_desc, link_prefix, link_info g_object_list, p_object_id, p_prior_object_id, p_object_name))

ORDER BY 1; END Open_Node_Obj_Pipe; PROCEDURE Open_Node_Obj ( p_node_id p_prior_link_info x_cur VARCHAR2, VARCHAR2, Network.curtyp) IS

OUT

l_object_id PLS_INTEGER := To_Number (Substr (p_node_id, Instr(p_node_id, c_delimiter, 1) + 1)); l_prior_object_id PLS_INTEGER := Nvl (To_Number (Substr (p_prior_link_info, Instr(p_prior_link_info, c_delimiter, 1) + 1)), 0); l_object_name VARCHAR2(30) := Substr (p_node_id, 1, Instr(p_node_id, c_delimiter, 1) - 1); BEGIN IF g_use_pipe THEN Open_Node_Obj_Pipe ( p_object_id p_prior_object_id p_object_name x_cur ELSE Open_Node_Obj_Array ( p_object_id p_prior_object_id p_object_name x_cur END IF; END Open_Node_Obj; 82007373.doc Page 33 of 37 => => => => l_object_id, l_prior_object_id, l_object_name, x_cur); => => => => l_object_id, l_prior_object_id, l_object_name, x_cur);

PROCEDURE Get_Node_Info_Con ( p_node_id x_node_info x_expand l_cnt l_lud l_date l_chr_cnt l_sel_str l_col_name BEGIN SELECT INTO FROM WHERE AND AND INTEGER; VARCHAR2(100); VARCHAR2(100); VARCHAR2(30); VARCHAR2(200); VARCHAR2(30);

OUT OUT

VARCHAR2, VARCHAR2, VARCHAR2) IS

Max(col.column_name) l_col_name user_tab_columns col col.column_name LIKE '%CREAT%DATE%' col.data_type = 'DATE' col.table_name = p_node_id;

IF l_col_name IS NULL THEN l_sel_str := 'SELECT Count(*), ''(no creation date)'' FROM '; ELSE l_sel_str := 'SELECT Count(*), Max(' || l_col_name || ') FROM '; END IF; EXECUTE IMMEDIATE l_sel_str || p_node_id INTO l_cnt, l_lud; l_chr_cnt := LTrim(To_Char (l_cnt, c_num_fmt)); IF l_col_name IS NULL THEN l_date := '; (no creation date)'; ELSE l_date := '; Last created ' || l_lud; END IF; IF l_cnt = 0 THEN x_node_info := c_empty; ELSIF l_cnt < c_large_threshold THEN x_node_info := '[Small - ' || l_chr_cnt || l_date || ']'; ELSE x_node_info := '[Large - ' || l_chr_cnt || l_date || ']'; END IF; IF x_node_info = c_empty THEN x_expand := 'N'; ELSE x_expand := 'Y'; END IF; IF g_do_expand_empty THEN x_expand := 'Y'; END IF; END Get_Node_Info_Con; PROCEDURE Get_Node_Info_Obj ( p_node_id x_node_info x_expand BEGIN x_node_info := ''; x_expand := 'Y'; END Get_Node_Info_Obj; PROCEDURE Open_Node ( p_node_id p_prior_link_info x_cur BEGIN IF g_net_type = 'CON' THEN Open_Node_Con (p_node_id => p_node_id, p_prior_link_info => p_prior_link_info, x_cur => x_cur); ELSIF g_net_type = 'OBJ' THEN 82007373.doc Page 34 of 37 VARCHAR2, VARCHAR2, Network.curtyp) IS VARCHAR2, VARCHAR2, VARCHAR2) IS

OUT OUT

OUT

Open_Node_Obj (p_node_id => p_node_id, p_prior_link_info => p_prior_link_info, x_cur => x_cur); ELSE RAISE_APPLICATION_ERROR (-20001, 'Invalid network type'); END IF; END Open_Node; PROCEDURE Get_Node_Info(p_node_id x_node_info x_expand BEGIN IF g_net_type = 'CON' THEN Get_Node_Info_Con (p_node_id => p_node_id, x_node_info => x_node_info, x_expand => x_expand); ELSIF g_net_type = 'OBJ' THEN Get_Node_Info_Obj (p_node_id => p_node_id, x_node_info => x_node_info, x_expand => x_expand); ELSE RAISE_APPLICATION_ERROR (-20001, 'Invalid network type'); END IF; END Get_Node_Info; END Network_Node; / SHO ERR OUT OUT VARCHAR2, VARCHAR2, VARCHAR2) IS

Client SQL
Foreign Key Network
SET LINES 180 SET PAGES 1000 SET SERVEROUTPUT ON COLUMN "Database" FORMAT A20 COLUMN "Time" FORMAT A20 COLUMN "Version" FORMAT A30 COLUMN "Session" FORMAT 9999990 COLUMN "OS User" FORMAT A10 COLUMN "Machine" FORMAT A20 SET SERVEROUTPUT ON SIZE 1000000 SPOOL L_FKs_Network SELECT 'Start: ' || To_Char(SYSDATE,'DD-MON-YYYY HH24:MI:SS') FROM DUAL; DEFINE TYPE='A' DEFINE MAX_LEVEL='0' DEFINE DO_EXPAND_EMPTY='Y' DECLARE TYPE list_type ref_list IS VARRAY(100) OF VARCHAR2(30); list_type := list_type( 'CUSTOMER' -- soademo 'SDO_COORD_REF_SYS' 'SDO_COORD_OPS' -- mdsys 'JOBS' );

----

BEGIN DBMS_Output.Put_line ('--- Tables within &MAX_LEVEL links from ' || ref_list.COUNT || ' reference tables:'); IF '&DO_EXPAND_EMPTY' = 'Y' THEN Network_Node.Init (p_do_expand_empty => TRUE); DBMS_Output.Put_line ('Expanding empty nodes'); ELSE Network_Node.Init (p_do_expand_empty => FALSE); DBMS_Output.Put_line ('Not expanding empty nodes'); END IF; FOR i IN 1..ref_list.COUNT LOOP IF Network.Expand_Network ( p_root_node 82007373.doc => ref_list(i), Page 35 of 37

p_max_level

=> &MAX_LEVEL) THEN

Network_Node.Write_Log ('*** Warning: Too many links, traversal aborted! ***'); END IF; Network.Write_Output; END LOOP; END; / SELECT 'End: ' || To_Char(SYSDATE,'DD-MON-YYYY HH24:MI:SS') FROM DUAL / SELECT line_text FROM ( SELECT line_ind, line_text FROM output_log ORDER BY 1) ilv / ROLLBACK / SPOOL OFF

Objects Network
SET LINES 180 SET PAGES 1000 SET SERVEROUTPUT ON COLUMN "Database" FORMAT A20 COLUMN "Time" FORMAT A20 COLUMN "Version" FORMAT A30 COLUMN "Session" FORMAT 9999990 COLUMN "OS User" FORMAT A10 COLUMN "Machine" FORMAT A20 SET SERVEROUTPUT ON SIZE 1000000 SPOOL L_Obj_Network SELECT 'Start: ' || To_Char(SYSDATE,'DD-MON-YYYY HH24:MI:SS') FROM DUAL; ALTER SESSION SET sql_trace = true; DEFINE TYPE='A' DEFINE MAX_LEVEL='0' REM 3/12/4 gives: Total 28120 links and 3654 distinct nodes DEFINE POS_BEG=3 DEFINE POS_END=12 DEFINE CNT_SAME_REQD=4 DECLARE TYPE list_type ref_list ---c_obj_criterion_400 c_obj_criterion_all BEGIN Network_Node.Init ( p_obj_criterion => c_obj_criterion_all, p_pos_beg => &POS_BEG, p_pos_end => &POS_END, p_cnt_same_reqd => &CNT_SAME_REQD, p_use_pipe => TRUE); FOR i IN 1..ref_list.COUNT LOOP Network_Node.Write_Log ('*** Root node: ' || ref_list(i)); IF Network.Expand_Network ( p_root_node p_max_level => ref_list(i), => &MAX_LEVEL) THEN IS VARRAY(100) OF VARCHAR2(30); list_type := list_type( '316|TABLE_PRIVILEGE_MAP' 'V$SEGMENT_STATISTICS|1422', 'V$SESS_TIME_MODEL|8825', 'V$SESSMETRIC|1484' ); CONSTANT VARCHAR2(5) := 'V$_%'; CONSTANT VARCHAR2(5) := '%';

Network_Node.Write_Log ('*** Warning: Too many links, traversal aborted! ***'); END IF; Network.Write_Output; END LOOP;

82007373.doc

Page 36 of 37

END; / ALTER SESSION SET sql_trace = FALSE; SELECT 'End PL/SQL: ' || To_Char(SYSDATE,'DD-MON-YYYY HH24:MI:SS') FROM DUAL / SELECT line_text FROM ( SELECT line_ind, line_text FROM output_log ORDER BY 1) ilv / SELECT 'End SELECT: ' || To_Char(SYSDATE,'DD-MON-YYYY HH24:MI:SS') FROM DUAL / ROLLBACK / SPOOL OFF

82007373.doc

Page 37 of 37